diff options
| author | Kamen Mladenov <kamen.d.mladenov@protonmail.com> | 2021-04-09 19:51:35 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-09 19:51:35 +0300 |
| commit | 233f38915ba0079079233eff55434ef349c05c45 (patch) | |
| tree | 6c5f69017865bcab87355e910c87339453da1406 | |
| parent | f4a70c6430db923af9fa9958a11c2d6612cb52cc (diff) | |
| parent | a992357efcf1bc1ece81b95ecee5e05a0b73bfdc (diff) | |
| download | DevHive-heroku/main.tar DevHive-heroku/main.tar.gz DevHive-heroku/main.zip | |
Merge pull request #28 from Team-Kaleidoscope/devHEADv0.2mainheroku/main
Second stage: Complete
389 files changed, 9280 insertions, 26949 deletions
@@ -354,4 +354,5 @@ MigrationBackup/ *.blob appsettings.Development.json **/.vscode/ -**/*.swp
\ No newline at end of file +**/*.swp +**/Logs @@ -2,18 +2,16 @@ DevHive is the social network solution for programmers. In it you can make posts to share with your friends, comment and more. -It's built with [`ASP.NET Core`](https://docs.microsoft.com/en-us/aspnet/core/introduction-to-aspnet-core?view=aspnetcore-5.0) as a back-end and [`Angular`](https://angular.io/) as a front-end. For more technical information, you can refer to the [Wiki](https://github.com/Team-Kaleidoscope/DevHive/wiki). +This is the back-end portion of the project, built with [`ASP.NET Core`](https://docs.microsoft.com/en-us/aspnet/core/introduction-to-aspnet-core?view=aspnetcore-5.0). For the front-end, refer to [DevHive-Angular](https://github.com/Team-Kaleidoscope/DevHive-Angular). -In the Wiki you can also find our [Privacy Policy](https://github.com/Team-Kaleidoscope/DevHive/wiki/Privacy-Policy). +In the `docs` folder you can find relevenat information and documentation about this project. You can also find our Privacy Policy. ## Contents: - [Setting up locally](#setting-up-locally) - [Prerequisites](#prerequisites) - [Getting started with the database](#getting-started-with-the-database) - - [Starting up the API](#starting-up-the-api) - - [Starting up the Front-end](#starting-up-the-front-end) + - [Starting up](#starting-up) - [Important notes](#important-notes) -- [Screenshots](#screenshots) ## Setting up locally @@ -23,9 +21,7 @@ The steps are oriented around Linux-based systems. ### Prerequisites -There are some things you need to setup before even downloading the app. - -Back-end (API) tools: +There are some tools you need to setup before even downloading the app: - [`dotnet 5.0`](https://docs.microsoft.com/en-us/dotnet/core/install/linux) or later - [`Entity Framework Core tool 5.0.2`](https://docs.microsoft.com/en-us/ef/core/cli/dotnet) or greater - if you've already installed `dotnet`, you can just run this command `dotnet tool install dotnet-ef -g` @@ -33,56 +29,26 @@ Back-end (API) tools: - Fedora users could also refer to [this](https://computingforgeeks.com/how-to-install-postgresql-12-on-fedora/) guide - Arch users can refer to the [ArchWiki](https://wiki.archlinux.org/index.php/PostgreSQL) -Front-end tools: -- [`Angular CLI 11.0.6`](https://www.tecmint.com/install-angular-cli-on-linux/) or greater (older versions might work, but haven't been tested) - ### Getting started with the database After installing all of the tools and setting up the database, you should have a user with a password with which to access postgres and you should have [created a database](https://www.tutorialspoint.com/postgresql/postgresql_create_database.htm) for the API. If so, follow these steps: 1. Clone the repository: `git clone https://github.com/Team-Kaleidoscope/DevHive.git` -2. Navigate to the folder, in which you cloned it, and then go to `src/DevHive.Data` +2. Navigate to the folder, in which you cloned it, and then go to `src/Data/DevHive.Data` 3. Edit the file `ConnectionString.json` and replace the default values with what you've setup 4. In the same directory, run `dotnet ef database update` -### Starting up the API +### Starting up -1. Navigate to the `src/DevHive.Web` folder -2. Edit the `appsettings.json` file, under "ConnectionStrings", type in the values, you setup in `ConnectionString.json` in `DevHive.Data` - - On the third row there is a "Secret" field, it's used for encryption of User passwords, you can change it **but it must be made up of 64 letters and number!** +1. Navigate to the `src/Web/DevHive.Web` folder +2. Edit the `appsettings.json` file, under "ConnectionStrings", type in the values, you setup in `ConnectionString.json` + - On the third row there is a "signingKey" field, it's used for encryption of User passwords. **It must be made up of 64 letters and number!** On the next two rows you also need to set a "validationIssuer" and "audience", you can set these values to whatever you want. - There are also some cloud values that you can change. Currently, the project uses [Cloudinary](https://cloudinary.com/) as a place for uploading files. If you wish, you can setup your own account there, otherwise file uploading won't work. -3. Run `dotnet run` in the `DevHive.Web` folder +3. Run `dotnet run` - feel free to [run the command in background](https://linuxize.com/post/how-to-run-linux-commands-in-background/) or [create a systemd service](https://medium.com/@benmorel/creating-a-linux-service-with-systemd-611b5c8b91d6) -If everything went well, you can now access the API on `http://localhost:5000/api` with something like [Postman](https://www.postman.com/) or the FOSS alternative [Insomnia Designer](https://github.com/Kong/insomnia). On where to send requests, refer to the [API Endpoints](https://github.com/Team-Kaleidoscope/DevHive/wiki/API-Endpoints) page in the Wiki. - -### Starting up the Front-end - -1. Navigate to `src/DevHive.Angular` -2. Run `npm install` to install all front-end packages -3. Run `ng serve` to start up the front-end - - as with the API, you can [run the command in background](https://linuxize.com/post/how-to-run-linux-commands-in-background/) or [create a systemd service](https://medium.com/@benmorel/creating-a-linux-service-with-systemd-611b5c8b91d6) - -If everything went smoothly, you will be able to access the front-end from `http://localhost:4200`. Also, don't forget that the API needs to be running *at the same time*, otherwise you won't get beyond the Login and Register pages! +If everything went well, you can now access the API on `http://localhost:5000/api` with something like [Postman](https://www.postman.com/) or the FOSS alternative [Insomnia Designer](https://github.com/Kong/insomnia). On where to send requests, refer to the "API-Endpoints" page in docs. ### Important notes -You can change on what port the API is ran, by changing the `HTTP_PORT` constant in `src/DevHive.Web/Program.cs`. If you do so, you **must** also update the front-end, because by default it will try to send requests to `http://localhost:5000`. -- Go to `src/DevHive.Angular/src/app` and edit the `app-constants.module.ts` file, on the second row you're gonna see the variable `BASE_API_URL`, change it accoring to your API modifications. - -You can change on what port the front-end is ran, by issuing the serve command with the `--port` parameter: `ng serve --port 5001`. But, **don't run it with the `--ssl` parameter!** SSL isn't supported yet and you'll have issues trying to use it! If you really need ssl, using a [reverse proxy](https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/) might be a viable alternative. - -## Screenshots - - - - - - - - - - - - - +You can change on what port the API is ran, by changing the `HTTP_PORT` constant in `src/Web/DevHive.Web/Program.cs`. If you do so, you **must** also update the front-end, because by default it will try to send requests to `http://localhost:5000`. diff --git a/docs/API-Endpoints.md b/docs/API-Endpoints.md new file mode 100644 index 0000000..503bfc4 --- /dev/null +++ b/docs/API-Endpoints.md @@ -0,0 +1,864 @@ +### Contents: +- [/api/User](#apiuser) + - [/Login](#login) + - [/Register](#register) + - [/GetUser](#getuser) + - [/ProfilePicture](#profilepicture) + - [Get User By Id](#get-user-by-id) + - [Update User By Id](#update-user-by-id) + - [Delete User By Id](#delete-user-by-id) +- [/api/Role](#apirole) + - [Create Role](#create-role) + - [Get Role By Id](#get-role-by-id) + - [Update Role By Id](#update-role-by-id) + - [Delete Role By Id](#delete-role-by-id) +- [/api/Feed](#apifeed) + - [/GetPosts](#getposts) + - [/GetUserPosts](#getuserposts) +- [/api/Post](#apipost) + - [Create Post](#create-post) + - [Get Post By Id](#get-post-by-id) + - [Update Post By Id](#update-post-by-id) + - [Delete Post By Id](#delete-post-by-id) +- [/api/Comment](#apicomment) + - [Create Comment](#create-comment) + - [Get Comment By Id](#get-comment-by-id) + - [Update Comment By Id](#update-comment-by-id) + - [Delete Comment By Id](#delete-comment-by-id) +- [/api/Language](#apilanguage) + - [/GetLanguages](#getlanguages) + - [Create Language](#create-language) + - [Get Language By Id](#get-language-by-id) + - [Update Language By Id](#update-language-by-id) + - [Delete Language By Id](#delete-language-by-id) +- [/api/Technology](#apitechnology) + - [/GetTechnologies](#gettechnologies) + - [Create Technology](#create-technology) + - [Get Technology By Id](#get-technology-by-id) + - [Update Technology By Id](#update-technology-by-id) + - [Delete Technology By Id](#delete-technology-by-id) + +*** + +# /api/User + +## /Login + +||| +|---|---| +|Description|Get a JWT for an existing user| +|Type|POST| +|URL structure|`http://localhost:5000/api/User/login`| +|Authentication|None| +|Body|JSON| +|Returns|JSON; The JWT| +||| + +Sample body: +```json +{ + "userName": "string", + "password": "string" +} +``` +Sample response: +```json +{ + "token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJJRCI6IjFiMTU2ZTY3LTVhZmItNGZmMi1hYzRmLTY4NjVmZWI5NzFiYiIsIlVzZXJuYW1lIjoieW9yZ3VzIiwicm9sZSI6IlVzZXIiLCJuYmYiOjE2MTIzMzg5NzIsImV4cCI6MTYxMjkwODAwMCwiaWF0IjoxNjEyMzM4OTcyfQ.dneQidggMu9FD7UXBzn5td3phX3OIgp7y4BygHTqq5Un5D67xH1jZTRQpi9Zqcq76mODvUToAo7j4PFdJtIdtg" +} +``` + +## /Register + +||| +|---|---| +|Description|Add an account to the database and get a JWT| +|Type|POST| +|URL structure|`http://localhost:5000/api/User/register`| +|Authentication|None| +|Body|JSON| +|Returns|JSON; The JWT| + +Sample body: +```json +{ + "userName": "string", + "email": "user@example.com", + "firstName": "string", + "lastName": "string", + "password": "string" +} +``` +Sample response: +```json +{ + "token": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJJRCI6IjFiMTU2ZTY3LTVhZmItNGZmMi1hYzRmLTY4NjVmZWI5NzFiYiIsIlVzZXJuYW1lIjoieW9yZ3VzIiwicm9sZSI6IlVzZXIiLCJuYmYiOjE2MTIzMzg5NzIsImV4cCI6MTYxMjkwODAwMCwiaWF0IjoxNjEyMzM4OTcyfQ.dneQidggMu9FD7UXBzn5td3phX3OIgp7y4BygHTqq5Un5D67xH1jZTRQpi9Zqcq76mODvUToAo7j4PFdJtIdtg" +} +``` + +## /GetUser + +||| +|---|---| +|Description|Get a user via his UserName| +|Type|GET| +|URL structure|`http://localhost:5000/api/User/GetUser?UserName=test`| +|Authentication|None| +|Body|None| +|Returns|JSON; The object of the user| +||| + +Sample response: +```json +{ + "profilePictureURL": "https://avatars.githubusercontent.com/u/75525529?s=60&v=4", + "roles": [ + { + "name": "User" + } + ], + "friends": [ + ], + "languages": [ + { + "id": "cf40034d-75d7-4792-821d-c16220a5b928" + } + ], + "technologies": [ + { + "id": "907421d9-1b60-411b-b780-85e65a004b56" + } + ], + "posts": [ + { + "id": "850d0655-72cb-4477-b69b-35e8645db266" + } + ], + "userName": "test", + "email": "test@bg.com", + "firstName": "Test", + "lastName": "Test" +} +``` + +## /ProfilePicture + +||| +|---|---| +|Description|Update the profile picture of the given User| +|Type|PUT| +|URL structure|`http://localhost:5000/api/User/ProfilePicture?UserId=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 2](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|Multipart Form| +|Returns|JSON; The link to the uploaded profile picture| +||| + +Sample body: +|Name|Value| +|---|---| +|Picture|`new-profile-picture.png` (this is the actual file)| + +Sample response: +```json +{ + "profilePictureURL": "https://avatars.githubusercontent.com/u/75525529?s=60&v=4" +} +``` + +## Get User By Id + +||| +|---|---| +|Description|Get a user via his Id| +|Type|GET| +|URL structure|`http://localhost:5000/api/User?Id=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 2](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|None| +|Returns|JSON; The object of the user| +||| + +Sample response: +```json +{ + "profilePictureURL": "https://avatars.githubusercontent.com/u/75525529?s=60&v=4", + "roles": [ + { + "name": "User" + } + ], + "friends": [ + ], + "languages": [ + { + "id": "cf40034d-75d7-4792-821d-c16220a5b928" + } + ], + "technologies": [ + { + "id": "907421d9-1b60-411b-b780-85e65a004b56" + } + ], + "posts": [ + { + "id": "850d0655-72cb-4477-b69b-35e8645db266" + } + ], + "userName": "test", + "email": "test@bg.com", + "firstName": "Test", + "lastName": "Test" +} +``` + +## Update User By Id + +||| +|---|---| +|Description|Modify the values in an existing user (account)| +|Type|PUT| +|URL structure|`http://localhost:5000/api/User?Id=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 2](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|JSON| +|Returns|JSON; The updated user object| +||| + +Sample body: +```json +{ + "userName": "string", + "email": "user@example.com", + "firstName": "string", + "lastName": "string", + "password": "string", + "friends": [ + { + "userName": "string" + } + ], + "roles": [ + { + "name": "string" + } + ], + "languages": [ + { + "name": "string" + } + ], + "technologies": [ + { + "name": "string" + } + ] +} +``` +Sample response: +```json +{ + "profilePictureURL": "https://avatars.githubusercontent.com/u/75525529?s=60&v=4", + "roles": [ + { + "name": "User" + } + ], + "friends": [], + "languages": [ + { + "id": "33397a3b-46eb-424f-8e19-d88dbf3e953b" + }, + { + "id": "cea85a74-4820-42ff-b64f-61d7e9bfc696" + } + ], + "technologies": [ + { + "id": "907421d9-1b60-411b-b780-85e65a004b56" + } + ], + "posts": [], + "userName": "test", + "email": "test1@bg.com", + "firstName": "Tester", + "lastName": "Tester" +} +``` + +## Delete User By Id + +||| +|---|---| +|Description|Delete an existing user from the database `WARNING: THIS IS IRREVERSIBLE`| +|Type|DELETE| +|URL structure|`http://localhost:5000/api/User?Id=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 2](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|None| +|Returns|Nothing| +||| + +*** + +# /api/Role + +## Create Role + +||| +|---|---| +|Description|Add a new Role to the DataBase| +|Type|POST| +|URL structure|`http://localhost:5000/api/Role`| +|Authentication|Bearer Token (JWT), [Authorization Type 3](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|JSON| +|Returns|JSON; The result role object, only with the role Id| +||| + +Sample body: +```json +{ + "name": "string" +} +``` +Sample response: +```json +{ + "id": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39" +} +``` + +## Get Role By Id + +||| +|---|---| +|Description|Get an existing Role via it's Id| +|Type|GET| +|URL structure|`http://localhost:5000/api/Role?Id=1cc9773f-8d9a-4bfd-83ca-2099dc787a39`| +|Authentication|Bearer Token (JWT), [Authorization Type 1](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|None| +|Returns|JSON; The role object, only with it's name| +||| + +Sample response: +```json +{ + "name": "Test" +} +``` + +## Update Role By Id + +||| +|---|---| +|Description|Modify the values (name) of an existing role| +|Type|PUT| +|URL structure|`http://localhost:5000/api/Role?Id=1cc9773f-8d9a-4bfd-83ca-2099dc787a39`| +|Authentication|Bearer Token (JWT), [Authorization Type 3](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|JSON| +|Returns|Nothing| +||| + +Sample body: +```json +{ + "name": "string" +} +``` + +## Delete Role By Id + +||| +|---|---| +|Description|Remove an existing Role from the DataBase| +|Type|POST| +|URL structure|`http://localhost:5000/api/Role?Id=1cc9773f-8d9a-4bfd-83ca-2099dc787a39`| +|Authentication|Bearer Token (JWT), [Authorization Type 3](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|None| +|Returns|Nothing| +||| + +*** + +# /api/Feed + +## /GetPosts + +||| +|---|---| +|Description|Get a certain amount of the latest posts of a User's friends| +|Type|POST| +|URL structure|`http://localhost:5000/api/Feed/GetPosts?UserId=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 1](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|JSON| +|Returns|JSON; An array with the selected posts| +||| + +Sample body: +```json +{ + "pageNumber": 1, + "firstPageTimeIssued": "2022-01-30T18:43:01.082Z", + "pageSize": 5 +} +``` +Sample response: +```json +{ + "posts": [ + { + "postId": "850d0655-72cb-4477-b69b-35e8645db266", + "creatorFirstName": "test", + "creatorLastName": "Test", + "creatorUsername": "Test", + "message": "A sample post", + "timeCreated": "2021-02-03T10:52:38.271647", + "comments": [], + "fileUrls": [] + } + ] +} +``` + +## /GetUserPosts + +||| +|---|---| +|Description|Get a certain amount of the latest posts from a User| +|Type|POST| +|URL structure|`http://localhost:5000/api/GetUserPosts?UserName=test`| +|Authentication|None| +|Body|JSON| +|Returns|JSON; An array with the selected posts| +||| + +Sample body: +```json +{ + "pageNumber": 1, + "firstPageTimeIssued": "2022-01-30T18:43:01.082Z", + "pageSize": 5 +} +``` +Sample response: +```json +{ + "posts": [ + { + "postId": "850d0655-72cb-4477-b69b-35e8645db266", + "creatorFirstName": "test", + "creatorLastName": "Test", + "creatorUsername": "Test", + "message": "A sample post", + "timeCreated": "2021-02-03T10:52:38.271647", + "comments": [], + "fileUrls": [] + } + ] +} +``` + +*** + +# /api/Post + +## Create Post + +||| +|---|---| +|Description|Add a new Post to the DataBase| +|Type|POST| +|URL structure|`http://localhost:5000/api/Post?UserId=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 1](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|Multipart Form| +|Returns|JSON; The result Post object, only with the Post Id| +||| + +Sample body: +|Name|Value| +|---|---| +|Message|The message of my post| +|Files|`attachment.txt` (that is the actual file)| +|Files|`attachment2.txt` (that is the actual file)| +|...|| + +Sample response: +```json +{ + "id": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39" +} +``` + +## Get Post By Id + +||| +|---|---| +|Description|Get an existing Post from the DataBase| +|Type|GET| +|URL structure|`http://localhost:5000/api/Post?Id=1cc9773f-8d9a-4bfd-83ca-2099dc787a39`| +|Authentication|None| +|Body|None| +|Returns|JSON; The result Post object| +||| + +Sample response: +```json +{ + "postId": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39", + "creatorFirstName": "Test", + "creatorLastName": "Test", + "creatorUsername": "test", + "message": "A sample post", + "timeCreated": "2021-02-02T18:29:31.942772", + "comments": [], + "fileUrls": [] +} +``` + +## Update Post By Id + +||| +|---|---| +|Description|Update the values of an existing post| +|Type|PUT| +|URL structure|`http://localhost:5000/api/Post?UserId=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 2](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|Multipart Form| +|Returns|JSON; The result Post object, only with the Post Id| +||| + +**Note:** When editing a post's files, they all get replaced, you cannot just add new files. After post is edited, it's "timeCreated" get's updated. + +Sample body: +|Name|Value| +|---|---| +|PostId|1cc9773f-8d9a-4bfd-83ca-2099dc787a39| +|NewMessage|The new message of the post| +|Files|`attachment3.txt` (that is the actual file)| +|Files|`attachment4.txt` (that is the actual file)| +|...| + +Sample response: +```json +{ + "id": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39" +} +``` + +## Delete Post By Id + +||| +|---|---| +|Description|Remove an existing Post from the DataBase| +|Type|DELETE| +|URL structure|`http://localhost:5000/api/Post?Id=1cc9773f-8d9a-4bfd-83ca-2099dc787a39`| +|Authentication|Bearer Token (JWT), [Authorization Type 2](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|None| +|Returns|None| +||| + +*** + +# /api/Comment + +## Add comment + +||| +|---|---| +|Description|Add a new Comment to an existing Post| +|Type|POST| +|URL structure|`http://localhost:5000/api/Comment?UserId=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 1](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|JSON| +|Returns|JSON; The result Comment object, only with the Comment Id| +||| + +Sample body: +```json +{ + "postId": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39", + "message": "First comment" +} +``` +Sample response: +```json +{ + "id": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39" +} +``` + +## Get Comment By Id + +||| +|---|---| +|Description|Get an existing Comment from the DataBase| +|Type|GET| +|URL structure|`http://localhost:5000/api/Comment?Id=1cc9773f-8d9a-4bfd-83ca-2099dc787a39`| +|Authentication|None| +|Body|None| +|Returns|JSON; The result Comment object| +||| + +Sample response: +```json +{ + "commentId": "086d1a23-c977-4cdc-9bdf-dc81992b3a12", + "postId": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39", + "issuerFirstName": "Test", + "issuerLastName": "Test", + "issuerUsername": "test", + "message": "First coment", + "timeCreated": "2021-02-01T13:18:57.434512" +} +``` + +## Update Comment By Id + +||| +|---|---| +|Description|Update the values of an existing comment| +|Type|PUT| +|URL structure|`http://localhost:5000/api/Comment?UserId=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 2](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|JSON| +|Returns|JSON; The result Comment object, only with the Comment Id| +||| + +Sample body: +```json +{ + "commentId": "086d1a23-c977-4cdc-9bdf-dc81992b3a12", + "postId": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39", + "newMessage": "string" +} +``` +Sample response: +```json +{ + "id": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39" +} +``` + +## Delete Comment By Id + +||| +|---|---| +|Description|Remove an existing Comment from the DataBase| +|Type|DELETE| +|URL structure|`http://localhost:5000/api/Comment?Id=086d1a23-c977-4cdc-9bdf-dc81992b3a12`| +|Authentication|Bearer Token (JWT), [Authorization Type 2](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|None| +|Returns|None| +||| + +*** + +# /api/Language + +## /GetLanguages + +||| +|---|---| +|Description|Get all available Languages from the DataBase| +|Type|GET| +|URL structure|`http://localhost:5000/api/Language/GetLanguages`| +|Authentication|Bearer Token (JWT), [Authorization Type 1](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|None| +|Returns|JSON; The result Language array object| +||| + +Sample response: +```json +[ + { + "id": "5286821a-1407-4daf-ac3e-49153c2d3f66", + "name": "CSharp" + }, + { + "id": "cea85a74-4820-42ff-b64f-61d7e9bfc696", + "name": "Perl" + }, + { + "id": "6dc1cb1a-1c4f-41af-8b44-86441cb60136", + "name": "Java" + } +] +``` + +## Create Language + +||| +|---|---| +|Description|Add a new Language in the DataBase| +|Type|POST| +|URL structure|`http://localhost:5000/api/Comment`| +|Authentication|Bearer Token (JWT), [Authorization Type 3](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|JSON| +|Returns|JSON; The result Comment object, only with the Comment Id| +||| + +Sample body: +```json +{ + "name": "Perl" +} +``` +Sample response: +```json +{ + "id": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39" +} +``` + +## Get Language By Id + +||| +|---|---| +|Description|Get an existing Language from the DataBase| +|Type|GET| +|URL structure|`http://localhost:5000/api/Language?Id=1cc9773f-8d9a-4bfd-83ca-2099dc787a39`| +|Authentication|None| +|Body|None| +|Returns|JSON; The result Language object| +||| + +Sample response: +```json +{ + "id": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39", + "name": "Perl" +} +``` + +## Update Language By Id + +||| +|---|---| +|Description|Update the values of an existing Language| +|Type|PUT| +|URL structure|`http://localhost:5000/api/Language?Id=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 3](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|JSON| +|Returns|None| +||| + +Sample body: +```json +{ + "name": "string" +} +``` + +## Delete Language By Id + +||| +|---|---| +|Description|Remove an existing Language from the DataBase| +|Type|DELETE| +|URL structure|`http://localhost:5000/api/Language?Id=086d1a23-c977-4cdc-9bdf-dc81992b3a12`| +|Authentication|Bearer Token (JWT), [Authorization Type 3](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|None| +|Returns|None| +||| + +*** + +# /api/Technology + +## /GetTechnologies + +||| +|---|---| +|Description|Get all available Technologies from the DataBase| +|Type|GET| +|URL structure|`http://localhost:5000/api/Language/GetTechnologies`| +|Authentication|Bearer Token (JWT), [Authorization Type 1](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|None| +|Returns|JSON; The result Technology array object| +||| + +Sample response: +```json +[ + { + "id": "5286821a-1407-4daf-ac3e-49153c2d3f66", + "name": "ASP.NET" + }, + { + "id": "cea85a74-4820-42ff-b64f-61d7e9bfc696", + "name": "Angular" + } +] +``` + +## Create Technology + +||| +|---|---| +|Description|Add a new Technology in the DataBase| +|Type|POST| +|URL structure|`http://localhost:5000/api/Technology`| +|Authentication|Bearer Token (JWT), [Authorization Type 3](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|JSON| +|Returns|JSON; The result Technology object, only with the Technology Id| +||| + +Sample body: +```json +{ + "name": "Angular" +} +``` +Sample response: +```json +{ + "id": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39" +} +``` + +## Get Technology By Id + +||| +|---|---| +|Description|Get an existing Technology from the DataBase| +|Type|GET| +|URL structure|`http://localhost:5000/api/Technology?Id=1cc9773f-8d9a-4bfd-83ca-2099dc787a39`| +|Authentication|None| +|Body|None| +|Returns|JSON; The result Technology object| +||| + +Sample response: +```json +{ + "id": "1cc9773f-8d9a-4bfd-83ca-2099dc787a39", + "name": "Angular" +} +``` + +## Update Technology By Id + +||| +|---|---| +|Description|Update the values of an existing Technology| +|Type|PUT| +|URL structure|`http://localhost:5000/api/Technology?Id=27e203bd-5312-4831-9334-cd3c20e5d672`| +|Authentication|Bearer Token (JWT), [Authorization Type 3](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|JSON| +|Returns|None| +||| + +Sample body: +```json +{ + "name": "string" +} +``` + +## Delete Technology By Id + +||| +|---|---| +|Description|Remove an existing Technology from the DataBase| +|Type|DELETE| +|URL structure|`http://localhost:5000/api/Technology?Id=086d1a23-c977-4cdc-9bdf-dc81992b3a12`| +|Authentication|Bearer Token (JWT), [Authorization Type 3](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication#token-validation)| +|Body|None| +|Returns|None| +|||
\ No newline at end of file diff --git a/docs/Authentication.md b/docs/Authentication.md new file mode 100644 index 0000000..f9e6525 --- /dev/null +++ b/docs/Authentication.md @@ -0,0 +1,52 @@ +Certain actions with the API require User authentication. In DevHive, all authentication is done with [JSON Web Tokens](https://en.wikipedia.org/wiki/JSON_Web_Token). + +The JWTs must be sent as a [Bearer Token](https://www.oauth.com/oauth2-servers/differences-between-oauth-1-2/bearer-tokens/). + +## Structure of tokens + +The main contents of a User's token are the `UserName`, `ID` and `Roles`. + +Sample token: +``` +eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJJRCI6IjI3ZTIwM2JkLTUzMTItNDgzMS05MzM0LWNkM2MyMGU1ZDY3MiIsIlVzZXJuYW1lIjoidGVzdCIsInJvbGUiOiJVc2VyIiwibmJmIjoxNjEyMzYxOTc1LCJleHAiOjE2MTI5MDgwMDAsImlhdCI6MTYxMjM2MTk3NX0.ZxhDSUsDf9cGig03QpzNgj3dkqbcfAoFXVIfixYGurzhd0l1_IO79UpE_Sb6ZU9hz3IT1XPrlrQ_Kd46L7xcQg +``` +[Decoded](https://jwt.io/): + +- Header +```json +{ + "alg": "HS512", + "typ": "JWT" +} +``` + +- Data +```json +{ + "ID": "27e203bd-5312-4831-9334-cd3c20e5d672", + "Username": "test", + "role": "User", + "nbf": 1612361975, + "exp": 1612908000, + "iat": 1612361975 +} +``` + +- Signature +``` +HMACSHA512( + base64UrlEncode(header) + "." + + base64UrlEncode(payload) +) +``` + +## Token validation + +All token validations are done in the User Service. Depending on the situation, we can differentiate a couple types of authentication: + +||| +|---|---| +|1|Has the role "User" or "Admin"| +|2|Has the role "User" and is the owner/author of the object or has the role "Admin"| +|3|Has the role "Admin"| +|||
\ No newline at end of file diff --git a/docs/File-Structure.md b/docs/File-Structure.md new file mode 100644 index 0000000..4d0341e --- /dev/null +++ b/docs/File-Structure.md @@ -0,0 +1,495 @@ +DevHive has a lot of files and it can be hard to really see the whole structure of everything from a first glance. In this document, all of the important/notable files and folders are represented in a tree structure. + +### Contents: +- [Common](#common) +- [Data](#data) +- [Service](#service) +- [Tests](#tests) +- [Web](#web) +- [Full Overview](#full-overview) + +# Common + +``` +└── DevHive.Common + └── Models + ├── Identity + │ └── TokenModel.cs + └── Misc + ├── IdModel.cs + ├── PasswordModifications.cs + └── Patch.cs +``` + +# Data + +``` +└── DevHive.Data + ├── ConnectionString.json + ├── DevHiveContext.cs + ├── DevHiveContextFactory.cs + ├── Interfaces + | ├── Models + | │ ├── IComment.cs + │ | ├── ILanguage.cs + │ | ├── IModel.cs + │ | ├── IPost.cs + │ | ├── IProfilePicture.cs + │ | ├── IRating.cs + │ | ├── IRole.cs + │ | ├── ITechnology.cs + │ | └── IUser.cs + | └── Repositories + | ├── ICommentRepository.cs + | ├── IFeedRepository.cs + | ├── ILanguageRepository.cs + | ├── IPostRepository.cs + | ├── IRatingRepository.cs + | ├── IRepository.cs + | ├── IRoleRepository.cs + | ├── ITechnologyRepository.cs + | └── IUserRepository.cs + ├── Migrations + ├── Models + │ ├── Comment.cs + │ ├── Language.cs + │ ├── Post.cs + │ ├── ProfilePicture.cs + │ ├── Rating.cs + │ ├── Role.cs + │ ├── Technology.cs + │ └── User.cs + ├── RelationModels + │ ├── RatedPost.cs + │ ├── UserFriend.cs + │ └── UserRate.cs + └── Repositories + ├── BaseRepository.cs + ├── CommentRepository.cs + ├── FeedRepository.cs + ├── LanguageRepository.cs + ├── PostRepository.cs + ├── RatingRepository.cs + ├── RoleRepository.cs + ├── TechnologyRepository.cs + └── UserRepository.cs +``` + +# Service + +``` +└── DevHive.Services + ├── Configurations + │ └── Mapping + | ├── CommentMappings.cs + | ├── FeedMappings.cs + | ├── LanguageMappings.cs + | ├── PostMappings.cs + | ├── RatingMappings.cs + | ├── RoleMapings.cs + | ├── TechnologyMappings.cs + | └── UserMappings.cs + ├── Interfaces + │ ├── ICloudService.cs + │ ├── ICommentService.cs + │ ├── IFeedService.cs + │ ├── ILanguageService.cs + │ ├── IPostService.cs + │ ├── IRateService.cs + │ ├── IRoleService.cs + │ ├── ITechnologyService.cs + │ └── IUserService.cs + ├── Models + | ├── Cloud + | │ └── CloudinaryService.cs + | ├── Comment + | │ ├── CreateCommentServiceModel.cs + | │ ├── ReadCommentServiceModel.cs + | │ └── UpdateCommentServiceModel.cs + | ├── Feed + | │ ├── GetPageServiceModel.cs + | │ └── ReadPageServiceModel.cs + | ├── Identity + | │ ├── Role + | │ │ ├── CreateRoleServiceModel.cs + | │ │ ├── RoleServiceModel.cs + | │ │ └── UpdateRoleServiceModel.cs + | │ └── User + | │ ├── BaseUserServiceModel.cs + | │ ├── FriendServiceModel.cs + | │ ├── LoginServiceModel.cs + | │ ├── ProfilePictureServiceModel.cs + | │ ├── RegisterServiceModel.cs + | │ ├── UpdateFriendServiceModel.cs + | │ ├── UpdateProfilePictureServiceModel.cs + | │ ├── UpdateUserServiceModel.cs + | │ └── UserServiceModel.cs + | ├── Language + | │ ├── CreateLanguageServiceModel.cs + | │ ├── LanguageServiceModel.cs + | │ ├── ReadLanguageServiceModel.cs + | │ └── UpdateLanguageServiceModel.cs + | ├── Post + | | ├── Rating + | | │ ├── RatePostServiceModel.cs + | | │ └── ReadPostRatingServiceModel.cs + | │ ├── CreatePostServiceModel.cs + | │ ├── ReadPostServiceModel.cs + | │ └── UpdatePostServiceModel.cs + | └── Technology + | ├── CreateTechnologyServiceModel.cs + | ├── ReadTechnologyServiceModel.cs + | ├── TechnologyServiceModel.cs + | └── UpdateTechnologyServiceModel.cs + ├── Options + │ └── JWTOptions.cs + └── Services + ├── CommentService.cs + ├── FeedService.cs + ├── LanguageService.cs + ├── PostService.cs + ├── RateService.cs + ├── RoleService.cs + ├── TechnologyService.cs + └── UserService.cs +``` + +# Tests + +``` +└── DevHive.Tests + ├── DevHive.Data.Tests + │ ├── CommentRepository.Tests.cs + │ ├── FeedRepository.Tests.cs + │ ├── LenguageRepository.Tests.cs + │ ├── PostRepository.Tests.cs + │ ├── RoleRepository.Tests.cs + │ ├── TechnologyRepository.Tests.cs + │ └── UserRepositoryTests.cs + ├── DevHive.Services.Tests + │ ├── FeedService.Tests.cs + │ ├── LanguageService.Tests.cs + │ ├── PostService.Tests.cs + │ ├── RoleService.Tests.cs + │ ├── TechnologyServices.Tests.cs + │ └── UserService.Tests.cs + └── DevHive.Web.Tests + ├── LanguageController.Tests.cs + └── TechnologyController.Tests.cs +``` + +# Web + +``` +└── DevHive.Web + ├── appsettings.json + ├── Attributes + │ ├── GoodPasswordModelValidation.cs + │ └── OnlyLettersModelValidation.cs + ├── Configurations + | ├── Extensions + | │ ├── ConfigureAutoMapper.cs + | │ ├── ConfigureDatabase.cs + | │ ├── ConfigureDependencyInjection.cs + | │ ├── ConfigureExceptionHandlerMiddleware.cs + | │ ├── ConfigureJWT.cs + | │ └── ConfigureSwagger.cs + | └── Mapping + | ├── CommentMappings.cs + | ├── FeedMappings.cs + | ├── LanguageMappings.cs + | ├── PostMappings.cs + | ├── RatingMappings.cs + | ├── RoleMappings.cs + | ├── TechnologyMappings.cs + | └── UserMappings.cs + ├── Controllers + │ ├── CommentController.cs + │ ├── FeedController.cs + │ ├── LanguageController.cs + │ ├── PostController.cs + │ ├── RateController.cs + │ ├── RoleController.cs + │ ├── TechnologyController.cs + │ └── UserController.cs + ├── Middleware + │ └── ExceptionMiddleware.cs + ├── Models + | ├── Comment + | │ ├── CreateCommentWebModel.cs + | │ ├── ReadCommentWebModel.cs + | │ └── UpdateCommentWebModel.cs + | ├── Feed + | │ ├── GetPageWebModel.cs + | │ └── ReadPageWebModel.cs + | ├── Identity + | │ ├── Role + | │ │ ├── CreateRoleWebModel.cs + | │ │ ├── RoleWebModel.cs + | │ │ └── UpdateRoleWebModel.cs + | │ └── User + | │ ├── BaseUserWebModel.cs + | │ ├── LoginWebModel.cs + | │ ├── ProfilePictureWebModel.cs + | │ ├── RegisterWebModel.cs + | │ ├── TokenWebModel.cs + | │ ├── UpdateProfilePictureWebModel.cs + | │ ├── UpdateUserWebModel.cs + | │ ├── UsernameWebModel.cs + | │ └── UserWebModel.cs + | ├── Language + | │ ├── CreateLanguageWebModel.cs + | │ ├── LanguageWebModel.cs + | │ ├── ReadLanguageWebModel.cs + | │ └── UpdateLanguageWebModel.cs + | ├── Post + | │ ├── Rating + | │ │ ├── RatePostWebModel.cs + | │ │ └── ReadPostRatingWebModel.cs + | │ ├── CreatePostWebModel.cs + | │ ├── ReadPostWebModel.cs + | │ └── UpdatePostWebModel.cs + | └── Technology + | ├── CreateTechnologyWebModel.cs + | ├── ReadTechnologyWebModel.cs + | ├── TechnologyWebModel.cs + | └── UpdateTechnologyWebModel.cs + ├── Program.cs + ├── Properties + │ └── launchSettings.json + └── Startup.cs +``` + +# Full overview + +``` +. +├── DevHive.code-workspace +├── DevHive.Common +| └── Models +| ├── Identity +| │ └── TokenModel.cs +| └── Misc +| ├── IdModel.cs +| ├── PasswordModifications.cs +| └── Patch.cs +├── DevHive.Data +│ ├── ConnectionString.json +│ ├── DevHiveContext.cs +│ ├── DevHiveContextFactory.cs +| ├── Interfaces +| | ├── Models +| | │ ├── IComment.cs +| │ | ├── ILanguage.cs +| │ | ├── IModel.cs +| │ | ├── IPost.cs +| │ | ├── IProfilePicture.cs +| │ | ├── IRating.cs +| │ | ├── IRole.cs +| │ | ├── ITechnology.cs +| │ | └── IUser.cs +| | └── Repositories +| | ├── ICommentRepository.cs +| | ├── IFeedRepository.cs +| | ├── ILanguageRepository.cs +| | ├── IPostRepository.cs +| | ├── IRatingRepository.cs +| | ├── IRepository.cs +| | ├── IRoleRepository.cs +| | ├── ITechnologyRepository.cs +| | └── IUserRepository.cs +│ ├── Migrations +│ ├── Models +│ │ ├── Comment.cs +│ │ ├── Language.cs +│ │ ├── Post.cs +│ │ ├── ProfilePicture.cs +│ │ ├── Rating.cs +│ │ ├── Role.cs +│ │ ├── Technology.cs +│ │ └── User.cs +│ ├── RelationModels +│ │ ├── RatedPost.cs +│ │ ├── UserFriend.cs +│ │ └── UserRate.cs +│ └── Repositories +│ ├── BaseRepository.cs +│ ├── CommentRepository.cs +│ ├── FeedRepository.cs +│ ├── LanguageRepository.cs +│ ├── PostRepository.cs +│ ├── RatingRepository.cs +│ ├── RoleRepository.cs +│ ├── TechnologyRepository.cs +│ └── UserRepository.cs +├── DevHive.Services +│ ├── Configurations +│ │ └── Mapping +| | ├── CommentMappings.cs +| | ├── FeedMappings.cs +| | ├── LanguageMappings.cs +| | ├── PostMappings.cs +| | ├── RatingMappings.cs +| | ├── RoleMapings.cs +| | ├── TechnologyMappings.cs +| | └── UserMappings.cs +│ ├── Interfaces +│ │ ├── ICloudService.cs +│ │ ├── ICommentService.cs +│ │ ├── IFeedService.cs +│ │ ├── ILanguageService.cs +│ │ ├── IPostService.cs +│ │ ├── IRateService.cs +│ │ ├── IRoleService.cs +│ │ ├── ITechnologyService.cs +│ │ └── IUserService.cs +│ ├── Models +| | ├── Cloud +| | │ └── CloudinaryService.cs +| | ├── Comment +| | │ ├── CreateCommentServiceModel.cs +| | │ ├── ReadCommentServiceModel.cs +| | │ └── UpdateCommentServiceModel.cs +| | ├── Feed +| | │ ├── GetPageServiceModel.cs +| | │ └── ReadPageServiceModel.cs +| | ├── Identity +| | │ ├── Role +| | │ │ ├── CreateRoleServiceModel.cs +| | │ │ ├── RoleServiceModel.cs +| | │ │ └── UpdateRoleServiceModel.cs +| | │ └── User +| | │ ├── BaseUserServiceModel.cs +| | │ ├── FriendServiceModel.cs +| | │ ├── LoginServiceModel.cs +| | │ ├── ProfilePictureServiceModel.cs +| | │ ├── RegisterServiceModel.cs +| | │ ├── UpdateFriendServiceModel.cs +| | │ ├── UpdateProfilePictureServiceModel.cs +| | │ ├── UpdateUserServiceModel.cs +| | │ └── UserServiceModel.cs +| | ├── Language +| | │ ├── CreateLanguageServiceModel.cs +| | │ ├── LanguageServiceModel.cs +| | │ ├── ReadLanguageServiceModel.cs +| | │ └── UpdateLanguageServiceModel.cs +| | ├── Post +| | | ├── Rating +| | | │ ├── RatePostServiceModel.cs +| | | │ └── ReadPostRatingServiceModel.cs +| | │ ├── CreatePostServiceModel.cs +| | │ ├── ReadPostServiceModel.cs +| | │ └── UpdatePostServiceModel.cs +| | └── Technology +| | ├── CreateTechnologyServiceModel.cs +| | ├── ReadTechnologyServiceModel.cs +| | ├── TechnologyServiceModel.cs +| | └── UpdateTechnologyServiceModel.cs +│ ├── Options +│ │ └── JWTOptions.cs +│ └── Services +│ ├── CommentService.cs +│ ├── FeedService.cs +│ ├── LanguageService.cs +│ ├── PostService.cs +│ ├── RateService.cs +│ ├── RoleService.cs +│ ├── TechnologyService.cs +│ └── UserService.cs +├── DevHive.Tests +│ ├── DevHive.Data.Tests +│ │ ├── CommentRepository.Tests.cs +│ │ ├── FeedRepository.Tests.cs +│ │ ├── LenguageRepository.Tests.cs +│ │ ├── PostRepository.Tests.cs +│ │ ├── RoleRepository.Tests.cs +│ │ ├── TechnologyRepository.Tests.cs +│ │ └── UserRepositoryTests.cs +│ ├── DevHive.Services.Tests +│ │ ├── FeedService.Tests.cs +│ │ ├── LanguageService.Tests.cs +│ │ ├── PostService.Tests.cs +│ │ ├── RoleService.Tests.cs +│ │ ├── TechnologyServices.Tests.cs +│ │ └── UserService.Tests.cs +│ └── DevHive.Web.Tests +│ ├── LanguageController.Tests.cs +│ └── TechnologyController.Tests.cs +└── DevHive.Web + ├── appsettings.json + ├── Attributes + │ ├── GoodPasswordModelValidation.cs + │ └── OnlyLettersModelValidation.cs + ├── Configurations + | ├── Extensions + | │ ├── ConfigureAutoMapper.cs + | │ ├── ConfigureDatabase.cs + | │ ├── ConfigureDependencyInjection.cs + | │ ├── ConfigureExceptionHandlerMiddleware.cs + | │ ├── ConfigureJWT.cs + | │ └── ConfigureSwagger.cs + | └── Mapping + | ├── CommentMappings.cs + | ├── FeedMappings.cs + | ├── LanguageMappings.cs + | ├── PostMappings.cs + | ├── RatingMappings.cs + | ├── RoleMappings.cs + | ├── TechnologyMappings.cs + | └── UserMappings.cs + ├── Controllers + │ ├── CommentController.cs + │ ├── FeedController.cs + │ ├── LanguageController.cs + │ ├── PostController.cs + │ ├── RateController.cs + │ ├── RoleController.cs + │ ├── TechnologyController.cs + │ └── UserController.cs + ├── Middleware + │ └── ExceptionMiddleware.cs + ├── Models + | ├── Comment + | │ ├── CreateCommentWebModel.cs + | │ ├── ReadCommentWebModel.cs + | │ └── UpdateCommentWebModel.cs + | ├── Feed + | │ ├── GetPageWebModel.cs + | │ └── ReadPageWebModel.cs + | ├── Identity + | │ ├── Role + | │ │ ├── CreateRoleWebModel.cs + | │ │ ├── RoleWebModel.cs + | │ │ └── UpdateRoleWebModel.cs + | │ └── User + | │ ├── BaseUserWebModel.cs + | │ ├── LoginWebModel.cs + | │ ├── ProfilePictureWebModel.cs + | │ ├── RegisterWebModel.cs + | │ ├── TokenWebModel.cs + | │ ├── UpdateProfilePictureWebModel.cs + | │ ├── UpdateUserWebModel.cs + | │ ├── UsernameWebModel.cs + | │ └── UserWebModel.cs + | ├── Language + | │ ├── CreateLanguageWebModel.cs + | │ ├── LanguageWebModel.cs + | │ ├── ReadLanguageWebModel.cs + | │ └── UpdateLanguageWebModel.cs + | ├── Post + | │ ├── Rating + | │ │ ├── RatePostWebModel.cs + | │ │ └── ReadPostRatingWebModel.cs + | │ ├── CreatePostWebModel.cs + | │ ├── ReadPostWebModel.cs + | │ └── UpdatePostWebModel.cs + | └── Technology + | ├── CreateTechnologyWebModel.cs + | ├── ReadTechnologyWebModel.cs + | ├── TechnologyWebModel.cs + | └── UpdateTechnologyWebModel.cs + ├── Program.cs + ├── Properties + │ └── launchSettings.json + └── Startup.cs +``` diff --git a/docs/Privacy-Policy.md b/docs/Privacy-Policy.md new file mode 100644 index 0000000..c338763 --- /dev/null +++ b/docs/Privacy-Policy.md @@ -0,0 +1,23 @@ +DevHive **doesn't collect any user data, that you haven't personally submitted** (there is no telemetry), and that won't ever change! + +The only potentially sensitive that that could be stored is your profile (first and last name, email, ..) and your posts (if you've shared anything sensitive), but in both cases you've personally given this information. + +## Data on the server + +All data is stored in the database and isn't shared with anyone. The entity that is hosting an instance of the application could expose data to unknown third parties, but DevHive doesn't do anything of the sorts by itself! + +## Data on your machine + +On your computer, the only thing that is saved is your [authentication token](https://github.com/Team-Kaleidoscope/DevHive/wiki/Authentication) in session storage. This is done so you could stay logged in the website in the current tab, and after closing it, the data gets deleted. + +In the future we could add a cookie to your computer storage, but that will still only hold the token for authentication purposes, so you can reopen your browser and still be logged in. **Tracking and third-party cookies are *never* going to be implemented!** + +## Telemetry by tools + +DevHive itself doesn't collect any type of telemetry, but that isn't the same for the tools it uses. + +The `dotnet` CLI tool is used to run the API, and `dotnet` does [`collect telemetry`](https://docs.microsoft.com/en-us/dotnet/core/tools/telemetry). [The same](https://angular.io/cli/usage-analytics-gathering) can be said for the Angular CLI (`ng`). + +Thankfully in both cases, you can opt out. Ask the administrator(s) of the instance you're using whether they have disabled telemetry. + +**Although**, it's important to mention that **this telemetry might not be collecting your data**, but the data of the server that uses it. Do your own research!
\ No newline at end of file diff --git a/screenshots/admin-panel.png b/screenshots/admin-panel.png Binary files differdeleted file mode 100644 index d8f1b3f..0000000 --- a/screenshots/admin-panel.png +++ /dev/null diff --git a/screenshots/another-user-logged-in.png b/screenshots/another-user-logged-in.png Binary files differdeleted file mode 100644 index 67f34e1..0000000 --- a/screenshots/another-user-logged-in.png +++ /dev/null diff --git a/screenshots/comment-page.png b/screenshots/comment-page.png Binary files differdeleted file mode 100644 index f517dc9..0000000 --- a/screenshots/comment-page.png +++ /dev/null diff --git a/screenshots/creating-post.png b/screenshots/creating-post.png Binary files differdeleted file mode 100644 index 58081f2..0000000 --- a/screenshots/creating-post.png +++ /dev/null diff --git a/screenshots/edit.png b/screenshots/edit.png Binary files differdeleted file mode 100644 index e448af3..0000000 --- a/screenshots/edit.png +++ /dev/null diff --git a/screenshots/feed.png b/screenshots/feed.png Binary files differdeleted file mode 100644 index a456e2a..0000000 --- a/screenshots/feed.png +++ /dev/null diff --git a/screenshots/login.png b/screenshots/login.png Binary files differdeleted file mode 100644 index 378a682..0000000 --- a/screenshots/login.png +++ /dev/null diff --git a/screenshots/post-page-with-comments.png b/screenshots/post-page-with-comments.png Binary files differdeleted file mode 100644 index d464053..0000000 --- a/screenshots/post-page-with-comments.png +++ /dev/null diff --git a/screenshots/post-page.png b/screenshots/post-page.png Binary files differdeleted file mode 100644 index 96428ef..0000000 --- a/screenshots/post-page.png +++ /dev/null diff --git a/screenshots/register.png b/screenshots/register.png Binary files differdeleted file mode 100644 index 70448b1..0000000 --- a/screenshots/register.png +++ /dev/null diff --git a/screenshots/your-profile-page.png b/screenshots/your-profile-page.png Binary files differdeleted file mode 100644 index 3701ea3..0000000 --- a/screenshots/your-profile-page.png +++ /dev/null diff --git a/screenshots/your-settings-page.png b/screenshots/your-settings-page.png Binary files differdeleted file mode 100644 index 61e39e6..0000000 --- a/screenshots/your-settings-page.png +++ /dev/null diff --git a/src/.dockerignore b/src/.dockerignore new file mode 100644 index 0000000..54310c0 --- /dev/null +++ b/src/.dockerignore @@ -0,0 +1,24 @@ +**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+README.md
diff --git a/src/.editorconfig b/src/.editorconfig index 7fa9b2a..9f0e74b 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -44,9 +44,10 @@ dotnet_diagnostic.IDE0055.severity = warning # Sort using and Import directives with System.* appearing first dotnet_sort_system_directives_first = true dotnet_separate_import_directive_groups = false + # Avoid "this." and "Me." if not necessary -dotnet_style_qualification_for_field = false:refactoring -dotnet_style_qualification_for_property = false:refactoring +dotnet_style_qualification_for_field = true:refactoring +dotnet_style_qualification_for_property = true:refactoring dotnet_style_qualification_for_method = false:refactoring dotnet_style_qualification_for_event = false:refactoring @@ -143,6 +144,7 @@ dotnet_naming_style.pascal_case_style.capitalization = pascal_case # error RS2008: Enable analyzer release tracking for the analyzer project containing rule '{0}' dotnet_diagnostic.RS2008.severity = none +dotnet_diagnostic.CS1591.severity = none # IDE0073: File header dotnet_diagnostic.IDE0073.severity = warning @@ -228,3 +230,6 @@ csharp_space_between_square_brackets = false # Wrapping preferences csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true + +[/Data/DevHive.Data/Migrations/**] +dotnet_diagnostic.IDE0055.severity = none diff --git a/src/.stylecop.json b/src/.stylecop.json new file mode 100644 index 0000000..6954967 --- /dev/null +++ b/src/.stylecop.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "indentation": { + "useTabs": true, + "tabSize": 4, + "indentationSize": 4 + }, + "orderingRules": { + "elementOrder": [ + "kind", + "accessibility", + "constant", + "static", + "readonly" + ], + "blankLinesBetweenUsingGroups": "require", + "systemUsingDirectivesFirst": true, + "usingDirectivesPlacement": "outsideNamespace" + }, + "namingRules": { + + } + } +} diff --git a/src/DevHive.Common/DevHive.Common.csproj b/src/Common/DevHive.Common.Models/DevHive.Common.Models.csproj index ace4997..db8d1c9 100644 --- a/src/DevHive.Common/DevHive.Common.csproj +++ b/src/Common/DevHive.Common.Models/DevHive.Common.Models.csproj @@ -1,15 +1,13 @@ -<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
- </PropertyGroup>
-
-<ItemGroup>
- <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.1" />
-</ItemGroup>
-
- <PropertyGroup>
- <EnableNETAnalyzers>true</EnableNETAnalyzers>
- <AnalysisLevel>latest</AnalysisLevel>
- </PropertyGroup>
-</Project>
+<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + <ItemGroup> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.2"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> + </ItemGroup> + <PropertyGroup> + <EnableNETAnalyzers>true</EnableNETAnalyzers> + <AnalysisLevel>latest</AnalysisLevel> + </PropertyGroup> +</Project>
\ No newline at end of file diff --git a/src/DevHive.Common/Models/Identity/TokenModel.cs b/src/Common/DevHive.Common.Models/Identity/TokenModel.cs index 0fb6c82..0fb6c82 100644 --- a/src/DevHive.Common/Models/Identity/TokenModel.cs +++ b/src/Common/DevHive.Common.Models/Identity/TokenModel.cs diff --git a/src/DevHive.Common/Models/Misc/IdModel.cs b/src/Common/DevHive.Common.Models/Misc/IdModel.cs index a5c7b65..a5c7b65 100644 --- a/src/DevHive.Common/Models/Misc/IdModel.cs +++ b/src/Common/DevHive.Common.Models/Misc/IdModel.cs diff --git a/src/DevHive.Common/Models/Misc/Patch.cs b/src/Common/DevHive.Common.Models/Misc/Patch.cs index ea5a4f1..ea5a4f1 100644 --- a/src/DevHive.Common/Models/Misc/Patch.cs +++ b/src/Common/DevHive.Common.Models/Misc/Patch.cs diff --git a/src/Common/DevHive.Common/Constants/ClassesConstants.cs b/src/Common/DevHive.Common/Constants/ClassesConstants.cs new file mode 100644 index 0000000..825f737 --- /dev/null +++ b/src/Common/DevHive.Common/Constants/ClassesConstants.cs @@ -0,0 +1,22 @@ +namespace DevHive.Common.Constants +{ + public static class ClassesConstants + { + public const string Data = "Data"; + + public const string Post = "The Post"; + public const string Comment = "The Comment"; + public const string User = "The User"; + public const string Language = "The Language"; + public const string Picture = "The Picture"; + public const string Files = "The Files"; + public const string Rating = "The Rating"; + public const string Role = "The Role"; + public const string Technology = "The Technology"; + + public const string Username = "The Username"; + public const string Email = "The Email"; + public const string Password = "Password"; + public const string OneOrMoreFriends = "One or more Friends"; + } +} diff --git a/src/Common/DevHive.Common/Constants/ErrorMessages.cs b/src/Common/DevHive.Common/Constants/ErrorMessages.cs new file mode 100644 index 0000000..30ca544 --- /dev/null +++ b/src/Common/DevHive.Common/Constants/ErrorMessages.cs @@ -0,0 +1,16 @@ +namespace DevHive.Common.Constants +{ + public static class ErrorMessages + { + public const string InvalidData = "Invalid {0}!"; + public const string IncorrectData = "Incorrect {0}!"; + public const string DoesNotExist = "{0} does not exist!"; + public const string AlreadyExists = "{0} already exists!"; + + public const string CannotAdd = "Could not add {0}!"; + public const string CannotCreate = "Could not create {0}!"; + public const string CannotDelete = "Could not delete {0}!"; + public const string CannotUpload = "Could not upload {0}!"; + public const string CannotEdit = "Could not edit {0}!"; + } +} diff --git a/src/Common/DevHive.Common/DevHive.Common.csproj b/src/Common/DevHive.Common/DevHive.Common.csproj new file mode 100644 index 0000000..a5758f4 --- /dev/null +++ b/src/Common/DevHive.Common/DevHive.Common.csproj @@ -0,0 +1,11 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <ItemGroup> + <ProjectReference Include="..\DevHive.Common.Models\DevHive.Common.Models.csproj"/> + </ItemGroup> + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + <ItemGroup> + <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.10.0"/> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/Common/DevHive.Common/Jwt/Interfaces/IJwtService.cs b/src/Common/DevHive.Common/Jwt/Interfaces/IJwtService.cs new file mode 100644 index 0000000..352a7d5 --- /dev/null +++ b/src/Common/DevHive.Common/Jwt/Interfaces/IJwtService.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace DevHive.Common.Jwt.Interfaces +{ + public interface IJwtService + { + /// <summary> + /// The generation of a JWT, when a new user registers or log ins + /// Tokens have an expiration time of 7 days. + /// </summary> + /// <param name="userId">User's Guid</param> + /// <param name="username">Users's username</param> + /// <param name="roleNames">List of user's roles</param> + /// <returns>Return a new JWT, containing the user id, username and roles.</returns> + string GenerateJwtToken(Guid userId, string username, List<string> roleNames); + + /// <summary> + /// Checks whether the given user, gotten by the "id" property, + /// is the same user as the one in the token (unless the user in the token has the admin role) + /// and the roles in the token are the same as those in the user, gotten by the id in the token + /// </summary> + /// <param name="userId">Guid of the user being validated</param> + /// <param name="rawToken">The raw token coming from the request</param> + /// <returns>Bool result of is the user authenticated to do an action</returns> + bool ValidateToken(Guid userId, string rawToken); + } +} diff --git a/src/Common/DevHive.Common/Jwt/JwtService.cs b/src/Common/DevHive.Common/Jwt/JwtService.cs new file mode 100644 index 0000000..9f316da --- /dev/null +++ b/src/Common/DevHive.Common/Jwt/JwtService.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using DevHive.Common.Jwt.Interfaces; +using Microsoft.IdentityModel.Tokens; + +namespace DevHive.Common.Jwt +{ + public class JwtService : IJwtService + { + private readonly string _validationIssuer; + private readonly string _audience; + private readonly byte[] _signingKey; + + public JwtService(byte[] signingKey, string validationIssuer, string audience) + { + this._signingKey = signingKey; + this._validationIssuer = validationIssuer; + this._audience = audience; + } + + public string GenerateJwtToken(Guid userId, string username, List<string> roleNames) + { + var securityKey = new SymmetricSecurityKey(this._signingKey); + var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); + + HashSet<Claim> claims = new() + { + new Claim("ID", $"{userId}"), + new Claim("Username", username) + }; + + foreach (var roleName in roleNames) + claims.Add(new Claim(ClaimTypes.Role, roleName)); + + SecurityTokenDescriptor securityTokenDescriptor = new() + { + Issuer = this._validationIssuer, + Audience = this._audience, + Subject = new ClaimsIdentity(claims), + Expires = DateTime.Today.AddDays(7), + SigningCredentials = credentials, + }; + + JwtSecurityTokenHandler tokenHandler = new(); + SecurityToken token = tokenHandler.CreateToken(securityTokenDescriptor); + + return tokenHandler.WriteToken(token); + } + + public bool ValidateToken(Guid userId, string rawToken) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var validationParameters = GetValidationParameters(); + string actualToken = rawToken.Remove(0, 7); + + IPrincipal principal = tokenHandler.ValidateToken(actualToken, validationParameters, out SecurityToken validatedToken); + JwtSecurityToken jwtToken = tokenHandler.ReadJwtToken(actualToken); + + if (!principal.Identity.IsAuthenticated) + return false; + else if (principal.IsInRole("Admin")) + return true; + else if (jwtToken.Claims.FirstOrDefault(x => x.Type == "ID").Value != userId.ToString()) + return false; + else + return true; + } + + private TokenValidationParameters GetValidationParameters() + { + return new TokenValidationParameters() + { + ValidateLifetime = true, + ValidateAudience = true, + ValidateIssuer = true, + ValidIssuer = this._validationIssuer, + ValidAudience = this._audience, + IssuerSigningKey = new SymmetricSecurityKey(this._signingKey) + }; + } + } +} diff --git a/src/DevHive.Data/Models/Comment.cs b/src/Data/DevHive.Data.Models/Comment.cs index e2bb21d..8a58edd 100644 --- a/src/DevHive.Data/Models/Comment.cs +++ b/src/Data/DevHive.Data.Models/Comment.cs @@ -1,16 +1,13 @@ using System; -using DevHive.Data.Interfaces.Models; namespace DevHive.Data.Models { - public class Comment : IComment + public class Comment { public Guid Id { get; set; } - // public Guid PostId { get; set; } public Post Post { get; set; } - // public Guid CreatorId { get; set; } public User Creator { get; set; } public string Message { get; set; } diff --git a/src/Data/DevHive.Data.Models/DevHive.Data.Models.csproj b/src/Data/DevHive.Data.Models/DevHive.Data.Models.csproj new file mode 100644 index 0000000..2958f86 --- /dev/null +++ b/src/Data/DevHive.Data.Models/DevHive.Data.Models.csproj @@ -0,0 +1,10 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + <ItemGroup> + <PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0"/> + <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.4"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/DevHive.Data/Models/Language.cs b/src/Data/DevHive.Data.Models/Language.cs index 7ad8ff2..b9e538a 100644 --- a/src/DevHive.Data/Models/Language.cs +++ b/src/Data/DevHive.Data.Models/Language.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; -using DevHive.Data.Interfaces.Models; namespace DevHive.Data.Models { - public class Language : ILanguage + public class Language { public Guid Id { get; set; } diff --git a/src/DevHive.Data/Models/Post.cs b/src/Data/DevHive.Data.Models/Post.cs index 3f3d8c9..a5e7642 100644 --- a/src/DevHive.Data/Models/Post.cs +++ b/src/Data/DevHive.Data.Models/Post.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using DevHive.Data.Interfaces.Models; -using DevHive.Data.RelationModels; +using DevHive.Data.Models.Relational; namespace DevHive.Data.Models { [Table("Posts")] - public class Post : IPost + public class Post { public Guid Id { get; set; } @@ -19,7 +18,7 @@ namespace DevHive.Data.Models public List<Comment> Comments { get; set; } = new(); - public Rating Rating { get; set; } = new(); + public List<Rating> Ratings { get; set; } public List<PostAttachments> Attachments { get; set; } = new(); } diff --git a/src/DevHive.Data/Models/ProfilePicture.cs b/src/Data/DevHive.Data.Models/ProfilePicture.cs index d5cc397..e8166d7 100644 --- a/src/DevHive.Data/Models/ProfilePicture.cs +++ b/src/Data/DevHive.Data.Models/ProfilePicture.cs @@ -1,10 +1,13 @@ using System; -using DevHive.Data.Interfaces.Models; +using System.ComponentModel.DataAnnotations.Schema; namespace DevHive.Data.Models { - public class ProfilePicture: IProfilePicture + [Table("ProfilePictures")] + public class ProfilePicture { + public const string DefaultURL = "/assets/icons/tabler-icon-user.svg"; + public Guid Id { get; set; } public Guid UserId { get; set; } diff --git a/src/Data/DevHive.Data.Models/Rating.cs b/src/Data/DevHive.Data.Models/Rating.cs new file mode 100644 index 0000000..c950697 --- /dev/null +++ b/src/Data/DevHive.Data.Models/Rating.cs @@ -0,0 +1,16 @@ +using System; + +namespace DevHive.Data.Models +{ + public class Rating + { + //if adding rating to comments change Post for intreface IRatable! + public Guid Id { get; set; } + + public User User { get; set; } + + public Post Post { get; set; } + + public bool IsLike { get; set; } + } +} diff --git a/src/DevHive.Data/RelationModels/PostAttachments.cs b/src/Data/DevHive.Data.Models/RelationalModels/PostAttachments.cs index 48aa8ff..8c814fc 100644 --- a/src/DevHive.Data/RelationModels/PostAttachments.cs +++ b/src/Data/DevHive.Data.Models/RelationalModels/PostAttachments.cs @@ -2,7 +2,7 @@ using System; using System.ComponentModel.DataAnnotations.Schema; using DevHive.Data.Models; -namespace DevHive.Data.RelationModels +namespace DevHive.Data.Models.Relational { [Table("PostAttachments")] public class PostAttachments diff --git a/src/DevHive.Data/Models/Role.cs b/src/Data/DevHive.Data.Models/Role.cs index 259d867..5456cb0 100644 --- a/src/DevHive.Data/Models/Role.cs +++ b/src/Data/DevHive.Data.Models/Role.cs @@ -1,13 +1,12 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Collections.Generic; -using DevHive.Data.Interfaces.Models; using Microsoft.AspNetCore.Identity; using System; namespace DevHive.Data.Models { [Table("Roles")] - public class Role : IdentityRole<Guid>, IRole + public class Role : IdentityRole<Guid> { public const string DefaultRole = "User"; public const string AdminRole = "Admin"; diff --git a/src/DevHive.Data/Models/Technology.cs b/src/Data/DevHive.Data.Models/Technology.cs index 6f98f0b..0dc077f 100644 --- a/src/DevHive.Data/Models/Technology.cs +++ b/src/Data/DevHive.Data.Models/Technology.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; -using DevHive.Data.Interfaces.Models; namespace DevHive.Data.Models { - public class Technology : ITechnology + public class Technology { public Guid Id { get; set; } diff --git a/src/DevHive.Data/Models/User.cs b/src/Data/DevHive.Data.Models/User.cs index 983d073..d3789ec 100644 --- a/src/DevHive.Data/Models/User.cs +++ b/src/Data/DevHive.Data.Models/User.cs @@ -1,20 +1,18 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using DevHive.Data.Interfaces.Models; -using DevHive.Data.RelationModels; using Microsoft.AspNetCore.Identity; namespace DevHive.Data.Models { [Table("Users")] - public class User : IdentityUser<Guid>, IUser + public class User : IdentityUser<Guid> { public string FirstName { get; set; } public string LastName { get; set; } - public ProfilePicture ProfilePicture { get; set; } + public ProfilePicture ProfilePicture { get; set; } = new() { PictureURL = ProfilePicture.DefaultURL }; public HashSet<Language> Languages { get; set; } = new(); @@ -27,7 +25,5 @@ namespace DevHive.Data.Models public HashSet<User> Friends { get; set; } = new(); public HashSet<Comment> Comments { get; set; } = new(); - - public HashSet<RatedPost> RatedPosts { get; set; } = new(); } } diff --git a/src/Data/DevHive.Data.Tests/CommentRepository.Tests.cs b/src/Data/DevHive.Data.Tests/CommentRepository.Tests.cs new file mode 100644 index 0000000..004d418 --- /dev/null +++ b/src/Data/DevHive.Data.Tests/CommentRepository.Tests.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DevHive.Data.Models; +using DevHive.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using NUnit.Framework; + +namespace DevHive.Data.Tests +{ + [TestFixture] + public class CommentRepositoryTests + { + private const string COMMENT_MESSAGE = "Comment message"; + private DevHiveContext _context; + private CommentRepository _commentRepository; + + #region SetUp + [SetUp] + public void Setup() + { + DbContextOptionsBuilder<DevHiveContext> optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() + .UseInMemoryDatabase(databaseName: "DevHive_Test_Database"); + + this._context = new DevHiveContext(optionsBuilder.Options); + + this._commentRepository = new CommentRepository(this._context); + } + + [TearDown] + public void TearDown() + { + this._context.Database.EnsureDeleted(); + } + #endregion + + #region GetByIdAsync + [Test] + public async Task GetByIdAsync_ReturnsTheCorrectComment_IfItExists() + { + Comment comment = await this.AddEntity(); + + Comment resultComment = await this._commentRepository.GetByIdAsync(comment.Id); + + Assert.AreEqual(comment.Id, resultComment.Id, "GetByIdAsync does not return the correct comment when it exists."); + } + + [Test] + public async Task GetByIdAsync_ReturnsNull_IfCommentDoesNotExist() + { + Comment resultComment = await this._commentRepository.GetByIdAsync(Guid.Empty); + + Assert.IsNull(resultComment, "GetByIdAsync does not return null when the comment does not exist"); + } + #endregion + + #region GetPostComments + [Test] + public async Task GetPostComments_ReturnsAllCommentsForPost_IfAnyExist() + { + List<Comment> comments = new List<Comment> + { + new Comment(), + new Comment(), + new Comment() + }; + Post post = new Post + { + Id = Guid.NewGuid(), + Comments = comments + }; + + this._context.Posts.Add(post); + await this._context.SaveChangesAsync(); + + List<Comment> resultComments = await this._commentRepository.GetPostComments(post.Id); + + Assert.AreEqual(comments.Count, resultComments.Count, "GetPostComments does not return the comments for a given post correctly"); + } + + [Test] + public async Task GetPostComments_ReturnsEmptyList_WhenPostDoesNotExist() + { + List<Comment> resultComments = await this._commentRepository.GetPostComments(Guid.Empty); + + Assert.IsEmpty(resultComments, "GetPostComments does not return empty string when post does not exist"); + } + #endregion + + #region GetCommentByIssuerAndTimeCreatedAsync + [Test] + public async Task GetCommentByCreatorAndTimeCreatedAsync_ReturnsTheCorrectComment_IfItExists() + { + Comment comment = await this.AddEntity(); + + Comment resultComment = await this._commentRepository.GetCommentByIssuerAndTimeCreatedAsync(comment.Creator.Id, comment.TimeCreated); + + Assert.AreEqual(comment.Id, resultComment.Id, "GetCommentByIssuerAndTimeCreatedAsync does not return the corect comment when it exists"); + } + + [Test] + public async Task GetPostByCreatorAndTimeCreatedAsync_ReturnsNull_IfThePostDoesNotExist() + { + Comment resultComment = await this._commentRepository.GetCommentByIssuerAndTimeCreatedAsync(Guid.Empty, DateTime.Now); + + Assert.IsNull(resultComment, "GetCommentByIssuerAndTimeCreatedAsync does not return null when the comment does not exist"); + } + #endregion + + #region EditAsync + [Test] + public async Task EditAsync_ReturnsTrue_WhenCommentIsUpdatedSuccessfully() + { + string newMessage = "New message!"; + Comment comment = await this.AddEntity(); + Comment updatedComment = new Comment + { + Id = comment.Id, + Message = newMessage + }; + + bool result = await this._commentRepository.EditAsync(comment.Id, updatedComment); + + Assert.IsTrue(result, "EditAsync does not return true when comment is updated successfully"); + } + #endregion + + #region DoesCommentExist + [Test] + public async Task DoesCommentExist_ReturnsTrue_WhenTheCommentExists() + { + Comment comment = await this.AddEntity(); + + bool result = await this._commentRepository.DoesCommentExist(comment.Id); + + Assert.IsTrue(result, "DoesCommentExist does not return true whenm the Comment exists"); + } + + [Test] + public async Task DoesCommentExist_ReturnsFalse_WhenTheCommentDoesNotExist() + { + bool result = await this._commentRepository.DoesCommentExist(Guid.Empty); + + Assert.IsFalse(result, "DoesCommentExist does not return false whenm the Comment" + + " does not exist"); + } + #endregion + + #region HelperMethods + private async Task<Comment> AddEntity() + { + User creator = new() { Id = Guid.NewGuid() }; + Comment comment = new() + { + Id = Guid.NewGuid(), + Message = COMMENT_MESSAGE, + Creator = creator, + TimeCreated = DateTime.Now + }; + + this._context.Comments.Add(comment); + await this._context.SaveChangesAsync(); + + return comment; + } + #endregion + } +} diff --git a/src/DevHive.Tests/DevHive.Data.Tests/DevHive.Data.Tests.csproj b/src/Data/DevHive.Data.Tests/DevHive.Data.Tests.csproj index d320450..e9b33e5 100644 --- a/src/DevHive.Tests/DevHive.Data.Tests/DevHive.Data.Tests.csproj +++ b/src/Data/DevHive.Data.Tests/DevHive.Data.Tests.csproj @@ -1,25 +1,21 @@ <Project Sdk="Microsoft.NET.Sdk"> - <PropertyGroup> <TargetFramework>net5.0</TargetFramework> - <IsPackable>false</IsPackable> </PropertyGroup> - <ItemGroup> - <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.1" /> - <PackageReference Include="Moq" Version="4.16.0" /> - <PackageReference Include="NUnit" Version="3.13.0" /> - <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.4"/> + <PackageReference Include="Moq" Version="4.16.1"/> + <PackageReference Include="NUnit" Version="3.13.1"/> + <PackageReference Include="NUnit3TestAdapter" Version="3.17.0"/> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\..\DevHive.Data\DevHive.Data.csproj" /> + <ProjectReference Include="..\DevHive.Data\DevHive.Data.csproj"/> </ItemGroup> - <PropertyGroup> <EnableNETAnalyzers>true</EnableNETAnalyzers> <AnalysisLevel>latest</AnalysisLevel> </PropertyGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/src/Data/DevHive.Data.Tests/FeedRepository.Tests.cs b/src/Data/DevHive.Data.Tests/FeedRepository.Tests.cs new file mode 100644 index 0000000..5d66bfc --- /dev/null +++ b/src/Data/DevHive.Data.Tests/FeedRepository.Tests.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DevHive.Data.Models; +using DevHive.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using NUnit.Framework; + +namespace DevHive.Data.Tests +{ + [TestFixture] + public class FeedRepositoryTests + { + private const int PAGE_NUMBER = 1; + private const int PAGE_SIZE = 10; + + private DevHiveContext _context; + private FeedRepository _feedRepository; + + #region Setups + [SetUp] + public void Setup() + { + DbContextOptionsBuilder<DevHiveContext> optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() + .UseInMemoryDatabase(databaseName: "DevHive_Test_Database"); + + this._context = new DevHiveContext(optionsBuilder.Options); + + this._feedRepository = new FeedRepository(this._context); + } + + [TearDown] + public void TearDown() + { + this._context.Database.EnsureDeleted(); + } + #endregion + + #region GetFriendsPosts + [Test] + public async Task GetFriendsPosts_ReturnsListOfPosts_WhenTheyExist() + { + User dummyUser = this.CreateDummyUser(); + dummyUser.Posts = await this.CreateDummyPosts(dummyUser); + List<User> friendsList = new() + { + dummyUser + }; + + DateTime dateTime = DateTime.Now; + + List<Post> resultList = await this._feedRepository.GetFriendsPosts(friendsList, dateTime, PAGE_NUMBER, PAGE_SIZE); + + Assert.GreaterOrEqual(resultList.Count, dummyUser.Posts.Count, "GetFriendsPosts does not return the posts corrtrectly"); + } + + [Test] + public async Task GetFriendsPosts_ReturnsEmptyList_WhenNoSuitablePostsExist() + { + User dummyUser = this.CreateDummyUser(); + List<User> friendsList = new() + { + dummyUser + }; + + DateTime dateTime = DateTime.Now; + + List<Post> resultList = await this._feedRepository.GetFriendsPosts(friendsList, dateTime, PAGE_NUMBER, PAGE_SIZE); + + Assert.LessOrEqual(resultList.Count, 0, "GetFriendsPosts does not return all correct posts"); + } + #endregion + + #region GetUsersPosts + [Test] + public async Task GetUsersPosts_ReturnsAllPostsOfTheUser_IfAnyExist() + { + User dummyUser = this.CreateDummyUser(); + HashSet<Post> posts = await this.CreateDummyPosts(dummyUser); + + DateTime dateTime = DateTime.Now; + + List<Post> resultList = await this._feedRepository.GetUsersPosts(dummyUser, dateTime, PAGE_NUMBER, PAGE_SIZE); + + Assert.GreaterOrEqual(resultList.Count, posts.Count, "GetUsersPosts does not return the posts corrtrectly"); + } + + [Test] + public async Task GetUsersPosts_ReturnsEmptyList_WhenNoSuitablePostsExist() + { + User dummyUser = this.CreateDummyUser(); + + DateTime dateTime = DateTime.Now; + + List<Post> resultList = await this._feedRepository.GetUsersPosts(dummyUser, dateTime, PAGE_NUMBER, PAGE_SIZE); + + Assert.LessOrEqual(resultList.Count, 0, "GetUsersPosts does not return empty list when no suitable posts exist"); + } + #endregion + + #region HelperMethods + private User CreateDummyUser() + { + HashSet<Role> roles = new() + { + new Role() + { + Id = Guid.NewGuid(), + Name = Role.DefaultRole + }, + }; + + return new() + { + Id = Guid.NewGuid(), + UserName = "pioneer10", + FirstName = "Spas", + LastName = "Spasov", + Email = "abv@abv.bg", + Roles = roles + }; + } + + private async Task<HashSet<Post>> CreateDummyPosts(User user) + { + HashSet<Post> posts = new HashSet<Post> + { + new Post { Creator = user, TimeCreated = DateTime.Now }, + new Post{ Creator = user, TimeCreated = DateTime.Now }, + new Post{ Creator = user, TimeCreated = DateTime.Now } + }; + + await this._context.Posts.AddRangeAsync(posts); + await this._context.SaveChangesAsync(); + + return posts; + } + #endregion + } +} diff --git a/src/DevHive.Tests/DevHive.Data.Tests/LenguageRepository.Tests.cs b/src/Data/DevHive.Data.Tests/LenguageRepository.Tests.cs index f02a1e4..c7d4dc7 100644 --- a/src/DevHive.Tests/DevHive.Data.Tests/LenguageRepository.Tests.cs +++ b/src/Data/DevHive.Data.Tests/LenguageRepository.Tests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using DevHive.Data.Models; @@ -12,25 +13,25 @@ namespace DevHive.Data.Tests public class LenguageRepositoryTests { private const string LANGUAGE_NAME = "Language test name"; - protected DevHiveContext Context { get; set; } - protected LanguageRepository LanguageRepository { get; set; } + private DevHiveContext _context; + private LanguageRepository _languageRepository; #region Setups [SetUp] public void Setup() { - var optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() + DbContextOptionsBuilder<DevHiveContext> optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() .UseInMemoryDatabase(databaseName: "DevHive_Test_Database"); - this.Context = new DevHiveContext(optionsBuilder.Options); + this._context = new DevHiveContext(optionsBuilder.Options); - LanguageRepository = new LanguageRepository(Context); + this._languageRepository = new LanguageRepository(this._context); } [TearDown] public void TearDown() { - this.Context.Database.EnsureDeleted(); + this._context.Database.EnsureDeleted(); } #endregion @@ -38,11 +39,11 @@ namespace DevHive.Data.Tests [Test] public async Task GetByNameAsync_ReturnsTheCorrectLanguage_IfItExists() { - await AddEntity(); + await this.AddEntity(); - Language language = this.Context.Languages.Where(x => x.Name == LANGUAGE_NAME).ToList().FirstOrDefault(); + Language language = this._context.Languages.Where(x => x.Name == LANGUAGE_NAME).AsEnumerable().FirstOrDefault(); - Language languageResult = await this.LanguageRepository.GetByNameAsync(LANGUAGE_NAME); + Language languageResult = await this._languageRepository.GetByNameAsync(LANGUAGE_NAME); Assert.AreEqual(language.Id, languageResult.Id); } @@ -50,22 +51,36 @@ namespace DevHive.Data.Tests [Test] public async Task GetByNameAsync_ReturnsNull_IfTechnologyDoesNotExists() { - Language languageResult = await this.LanguageRepository.GetByNameAsync(LANGUAGE_NAME); + Language languageResult = await this._languageRepository.GetByNameAsync(LANGUAGE_NAME); Assert.IsNull(languageResult); } #endregion + #region GetLanguages + [Test] + public async Task GetLanguages_ReturnsAllLanguages() + { + await this.AddEntity(); + await this.AddEntity("secondLanguage"); + await this.AddEntity("thirdLanguage"); + + HashSet<Language> languages = this._languageRepository.GetLanguages(); + + Assert.GreaterOrEqual(languages.Count, 3, "GetLanguages does not get all Languages"); + } + #endregion + #region DoesLanguageExistAsync [Test] public async Task DoesLanguageExist_ReturnsTrue_IfIdExists() { - await AddEntity(); - Language language = this.Context.Languages.Where(x => x.Name == LANGUAGE_NAME).ToList().FirstOrDefault(); + await this.AddEntity(); + Language language = this._context.Languages.Where(x => x.Name == LANGUAGE_NAME).AsEnumerable().FirstOrDefault(); Guid id = language.Id; - bool result = await this.LanguageRepository.DoesLanguageExistAsync(id); + bool result = await this._languageRepository.DoesLanguageExistAsync(id); Assert.IsTrue(result, "DoesLanguageExistAsync returns flase when language exists"); } @@ -75,7 +90,7 @@ namespace DevHive.Data.Tests { Guid id = Guid.NewGuid(); - bool result = await this.LanguageRepository.DoesLanguageExistAsync(id); + bool result = await this._languageRepository.DoesLanguageExistAsync(id); Assert.IsFalse(result, "DoesLanguageExistAsync returns true when language does not exist"); } @@ -85,9 +100,9 @@ namespace DevHive.Data.Tests [Test] public async Task DoesLanguageNameExist_ReturnsTrue_IfLanguageExists() { - await AddEntity(); + await this.AddEntity(); - bool result = await this.LanguageRepository.DoesLanguageNameExistAsync(LANGUAGE_NAME); + bool result = await this._languageRepository.DoesLanguageNameExistAsync(LANGUAGE_NAME); Assert.IsTrue(result, "DoesLanguageNameExists returns true when language name does not exist"); } @@ -95,7 +110,7 @@ namespace DevHive.Data.Tests [Test] public async Task DoesLanguageNameExist_ReturnsFalse_IfLanguageDoesNotExists() { - bool result = await this.LanguageRepository.DoesLanguageNameExistAsync(LANGUAGE_NAME); + bool result = await this._languageRepository.DoesLanguageNameExistAsync(LANGUAGE_NAME); Assert.False(result, "DoesTechnologyNameExistAsync returns true when language name does not exist"); } @@ -104,12 +119,14 @@ namespace DevHive.Data.Tests #region HelperMethods private async Task AddEntity(string name = LANGUAGE_NAME) { - Language language = new Language + Language language = new() { + Id = Guid.NewGuid(), Name = name }; - await this.LanguageRepository.AddAsync(language); + await this._context.Languages.AddAsync(language); + await this._context.SaveChangesAsync(); } #endregion } diff --git a/src/DevHive.Tests/DevHive.Data.Tests/PostRepository.Tests.cs b/src/Data/DevHive.Data.Tests/PostRepository.Tests.cs index 6dacf0b..005769f 100644 --- a/src/DevHive.Tests/DevHive.Data.Tests/PostRepository.Tests.cs +++ b/src/Data/DevHive.Data.Tests/PostRepository.Tests.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using DevHive.Data.Interfaces.Repositories; +using DevHive.Data.Interfaces; using DevHive.Data.Models; -using DevHive.Data.RelationModels; +using DevHive.Data.Models.Relational; using DevHive.Data.Repositories; using Microsoft.EntityFrameworkCore; using Moq; @@ -11,35 +11,32 @@ using NUnit.Framework; namespace DevHive.Data.Tests { - [TestFixture] + [TestFixture] public class PostRepositoryTests { private const string POST_MESSAGE = "Post test message"; - - private DevHiveContext Context { get; set; } - - private Mock<IUserRepository> UserRepository { get; set; } - - private PostRepository PostRepository { get; set; } + private DevHiveContext _context; + private Mock<IUserRepository> _userRepository; + private PostRepository _postRepository; #region Setups [SetUp] public void Setup() { - var optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() + DbContextOptionsBuilder<DevHiveContext> optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() .UseInMemoryDatabase(databaseName: "DevHive_Test_Database"); - this.Context = new DevHiveContext(optionsBuilder.Options); + this._context = new DevHiveContext(optionsBuilder.Options); - this.UserRepository = new Mock<IUserRepository>(); + this._userRepository = new Mock<IUserRepository>(); - PostRepository = new PostRepository(Context, this.UserRepository.Object); + this._postRepository = new PostRepository(this._context, this._userRepository.Object); } [TearDown] public void TearDown() { - this.Context.Database.EnsureDeleted(); + this._context.Database.EnsureDeleted(); } #endregion @@ -49,11 +46,11 @@ namespace DevHive.Data.Tests // { // Post post = await this.AddEntity(); // User user = new User { Id = Guid.NewGuid() }; - // + // // this.UserRepository.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(user)); - // + // // bool result = await this.PostRepository.AddNewPostToCreator(user.Id, post); - // + // // Assert.IsTrue(result, "AddNewPostToCreator does not return true when Post Is Added To Creator successfully"); // } #endregion @@ -62,9 +59,9 @@ namespace DevHive.Data.Tests [Test] public async Task GetByNameAsync_ReturnsTheCorrectPost_IfItExists() { - Post post = await AddEntity(); + Post post = await this.AddEntity(); - Post resultTechnology = await this.PostRepository.GetByIdAsync(post.Id); + Post resultTechnology = await this._postRepository.GetByIdAsync(post.Id); Assert.AreEqual(post.Id, resultTechnology.Id, "GetByIdAsync does not return the correct post"); } @@ -72,7 +69,7 @@ namespace DevHive.Data.Tests [Test] public async Task GetByIdAsync_ReturnsNull_IfTechnologyDoesNotExists() { - Post resultPost = await this.PostRepository.GetByIdAsync(Guid.NewGuid()); + Post resultPost = await this._postRepository.GetByIdAsync(Guid.NewGuid()); Assert.IsNull(resultPost); } @@ -84,7 +81,7 @@ namespace DevHive.Data.Tests { Post post = await this.AddEntity(); - Post resultPost = await this.PostRepository.GetPostByCreatorAndTimeCreatedAsync(post.Creator.Id, post.TimeCreated); + Post resultPost = await this._postRepository.GetPostByCreatorAndTimeCreatedAsync(post.Creator.Id, post.TimeCreated); Assert.AreEqual(post.Id, resultPost.Id, "GetPostByCreatorAndTimeCreatedAsync does not return the corect post when it exists"); } @@ -92,9 +89,9 @@ namespace DevHive.Data.Tests [Test] public async Task GetPostByCreatorAndTimeCreatedAsync_ReturnsNull_IfThePostDoesNotExist() { - Post post = await this.AddEntity(); + await this.AddEntity(); - Post resutPost = await this.PostRepository.GetPostByCreatorAndTimeCreatedAsync(Guid.Empty, DateTime.Now); + Post resutPost = await this._postRepository.GetPostByCreatorAndTimeCreatedAsync(Guid.Empty, DateTime.Now); Assert.IsNull(resutPost, "GetPostByCreatorAndTimeCreatedAsync does not return null when the post does not exist"); } @@ -106,7 +103,7 @@ namespace DevHive.Data.Tests { Post post = await this.AddEntity(); - bool result = await this.PostRepository.DoesPostExist(post.Id); + bool result = await this._postRepository.DoesPostExist(post.Id); Assert.IsTrue(result, "DoesPostExist does not return true whenm the Post exists"); } @@ -114,18 +111,18 @@ namespace DevHive.Data.Tests [Test] public async Task DoesPostExist_ReturnsFalse_WhenThePostDoesNotExist() { - bool result = await this.PostRepository.DoesPostExist(Guid.Empty); + bool result = await this._postRepository.DoesPostExist(Guid.Empty); Assert.IsFalse(result, "DoesPostExist does not return false whenm the Post does not exist"); } #endregion #region HelperMethods - private async Task<Post> AddEntity(string name = POST_MESSAGE) + private async Task<Post> AddEntity() { - User creator = new User { Id = Guid.NewGuid() }; - await this.Context.Users.AddAsync(creator); - Post post = new Post + User creator = new() { Id = Guid.NewGuid() }; + await this._context.Users.AddAsync(creator); + Post post = new() { Message = POST_MESSAGE, Id = Guid.NewGuid(), @@ -135,8 +132,8 @@ namespace DevHive.Data.Tests Comments = new List<Comment>() }; - await this.Context.Posts.AddAsync(post); - await this.Context.SaveChangesAsync(); + await this._context.Posts.AddAsync(post); + await this._context.SaveChangesAsync(); return post; } diff --git a/src/Data/DevHive.Data.Tests/RatingRepository.Tests.cs b/src/Data/DevHive.Data.Tests/RatingRepository.Tests.cs new file mode 100644 index 0000000..b5299b0 --- /dev/null +++ b/src/Data/DevHive.Data.Tests/RatingRepository.Tests.cs @@ -0,0 +1,188 @@ +using DevHive.Data.Repositories; +using Microsoft.EntityFrameworkCore; +using NUnit.Framework; +using System.Threading.Tasks; +using DevHive.Data.Models; +using System.Linq; +using System; +using System.Collections.Generic; + +namespace DevHive.Data.Tests +{ + [TestFixture] + public class RatingRepositoryTests + { + private DevHiveContext _context; + private RatingRepository _ratingRepository; + + #region Setups + [SetUp] + public void Setup() + { + var optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() + .UseInMemoryDatabase(databaseName: "DevHive_Test_Database"); + + this._context = new DevHiveContext(optionsBuilder.Options); + this._ratingRepository = new RatingRepository(this._context); + } + + [TearDown] + public void TearDown() + { + this._context.Database.EnsureDeleted(); + } + #endregion + + #region GetById + [Test] + public async Task GetByIdAsync_ReturnsTheCorrectRating_IfItExists() + { + Guid ratingId = Guid.NewGuid(); + await AddDummyRating(ratingId); + + Rating ratingResult = await this._ratingRepository.GetByIdAsync(ratingId); + + Assert.AreEqual(ratingResult.Id, ratingId); + } + + [Test] + public async Task GetByIdAsync_ReturnsNull_IfRatingDoesNotExist() + { + Rating ratingResult = await this._ratingRepository.GetByIdAsync(Guid.NewGuid()); + + Assert.IsNull(ratingResult); + } + #endregion + + #region GetByPostId + [Test] + public async Task GetRatingsByPostId_ReturnsFilledListOfRatings_WhenTheyExist() + { + Guid postId = Guid.NewGuid(); + await AddDummyPost(postId); + await AddDummyRating(Guid.NewGuid(), postId); + await AddDummyRating(Guid.NewGuid(), postId); + + List<Rating> result = await this._ratingRepository.GetRatingsByPostId(postId); + + Assert.IsNotEmpty(result); + } + + [Test] + public async Task GetRatingsByPostId_ReturnsEmptyList_WhenThereAreNoRatings() + { + List<Rating> result = await this._ratingRepository.GetRatingsByPostId(Guid.NewGuid()); + + Assert.IsEmpty(result); + } + #endregion + + #region GetByUserAndPostId + [Test] + public async Task GetRatingByUserAndPostId_ReturnsRating_WhenItExists() + { + Guid ratingId = Guid.NewGuid(); + Guid postId = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + await AddDummyPost(postId); + await AddDummyUser(userId); + await AddDummyRating(ratingId, postId, userId); + + Rating result = await this._ratingRepository.GetRatingByUserAndPostId(userId, postId); + + Assert.AreEqual(result.Id, ratingId); + } + + [Test] + public async Task GetRatingByUserAndPostId_ReturnsNull_WhenRatingDoesNotExist() + { + Rating result = await this._ratingRepository.GetRatingByUserAndPostId(Guid.NewGuid(), Guid.NewGuid()); + + Assert.IsNull(result); + } + #endregion + + #region UserRatedPost + [Test] + public async Task UserRatedPost_ReturnsTrue_WhenUserHasRatedPost() + { + Guid postId = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + await AddDummyPost(postId); + await AddDummyUser(userId); + await AddDummyRating(Guid.NewGuid(), postId, userId); + + bool result = await this._ratingRepository.UserRatedPost(userId, postId); + + Assert.IsTrue(result); + } + + [Test] + public async Task UserRatedPost_ReturnsFalse_WhenUserHasNotRatedPost() + { + bool result = await this._ratingRepository.UserRatedPost(Guid.NewGuid(), Guid.NewGuid()); + + Assert.IsFalse(result); + } + #endregion + + #region DoesRatingExist + [Test] + public async Task DoesRatingExist_ReturnsTrue_WhenItExists() + { + Guid ratingId = Guid.NewGuid(); + await AddDummyRating(ratingId); + + bool result = await this._ratingRepository.DoesRatingExist(ratingId); + + Assert.IsTrue(result); + } + + [Test] + public async Task DoesRatingExist_ReturnsFalse_WhenRatingDoesNotExist() + { + bool result = await this._ratingRepository.DoesRatingExist(Guid.NewGuid()); + + Assert.IsFalse(result); + } + #endregion + + #region HelperMethods + private async Task AddDummyRating(Guid ratingId, Guid postId = default(Guid), Guid userId = default(Guid)) + { + Rating rating = new Rating + { + Id = ratingId, + Post = this._context.Posts.FirstOrDefault(x => x.Id == postId), + User = this._context.Users.FirstOrDefault(x => x.Id == userId) + }; + + await this._context.Rating.AddAsync(rating); + await this._context.SaveChangesAsync(); + } + + private async Task AddDummyPost(Guid postId) + { + Post post = new Post() + { + Id = postId, + Message = "Never gonna give you up" + }; + + await this._context.Posts.AddAsync(post); + await this._context.SaveChangesAsync(); + } + + private async Task AddDummyUser(Guid userId) + { + User user = new User() + { + Id = userId + }; + + await this._context.Users.AddAsync(user); + await this._context.SaveChangesAsync(); + } + #endregion + } +} diff --git a/src/DevHive.Tests/DevHive.Data.Tests/TechnologyRepository.Tests.cs b/src/Data/DevHive.Data.Tests/TechnologyRepository.Tests.cs index d25fd3b..d268777 100644 --- a/src/DevHive.Tests/DevHive.Data.Tests/TechnologyRepository.Tests.cs +++ b/src/Data/DevHive.Data.Tests/TechnologyRepository.Tests.cs @@ -12,27 +12,25 @@ namespace DevHive.Data.Tests public class TechnologyRepositoryTests { private const string TECHNOLOGY_NAME = "Technology test name"; - - protected DevHiveContext Context { get; set; } - - protected TechnologyRepository TechnologyRepository { get; set; } + private DevHiveContext _context; + private TechnologyRepository _technologyRepository; #region Setups [SetUp] public void Setup() { - var optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() + DbContextOptionsBuilder<DevHiveContext> optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() .UseInMemoryDatabase(databaseName: "DevHive_Test_Database"); - this.Context = new DevHiveContext(optionsBuilder.Options); + this._context = new DevHiveContext(optionsBuilder.Options); - TechnologyRepository = new TechnologyRepository(Context); + this._technologyRepository = new TechnologyRepository(this._context); } [TearDown] public void TearDown() { - this.Context.Database.EnsureDeleted(); + this._context.Database.EnsureDeleted(); } #endregion @@ -40,11 +38,11 @@ namespace DevHive.Data.Tests [Test] public async Task GetByNameAsync_ReturnsTheCorrectTechnology_IfItExists() { - await AddEntity(); + await this.AddEntity(); - Technology technology = this.Context.Technologies.Where(x => x.Name == TECHNOLOGY_NAME).ToList().FirstOrDefault(); + Technology technology = this._context.Technologies.Where(x => x.Name == TECHNOLOGY_NAME).AsEnumerable().FirstOrDefault(); - Technology resultTechnology = await this.TechnologyRepository.GetByNameAsync(TECHNOLOGY_NAME); + Technology resultTechnology = await this._technologyRepository.GetByNameAsync(TECHNOLOGY_NAME); Assert.AreEqual(technology.Id, resultTechnology.Id); } @@ -52,7 +50,7 @@ namespace DevHive.Data.Tests [Test] public async Task GetByNameAsync_ReturnsNull_IfTechnologyDoesNotExists() { - Technology resultTechnology = await this.TechnologyRepository.GetByNameAsync(TECHNOLOGY_NAME); + Technology resultTechnology = await this._technologyRepository.GetByNameAsync(TECHNOLOGY_NAME); Assert.IsNull(resultTechnology); } @@ -62,11 +60,11 @@ namespace DevHive.Data.Tests [Test] public async Task DoesTechnologyExist_ReturnsTrue_IfIdExists() { - await AddEntity(); - Technology technology = this.Context.Technologies.Where(x => x.Name == TECHNOLOGY_NAME).ToList().FirstOrDefault(); + await this.AddEntity(); + Technology technology = this._context.Technologies.Where(x => x.Name == TECHNOLOGY_NAME).AsEnumerable().FirstOrDefault(); Guid id = technology.Id; - bool result = await this.TechnologyRepository.DoesTechnologyExistAsync(id); + bool result = await this._technologyRepository.DoesTechnologyExistAsync(id); Assert.IsTrue(result, "DoesTechnologyExistAsync returns flase hwen technology exists"); } @@ -76,7 +74,7 @@ namespace DevHive.Data.Tests { Guid id = Guid.NewGuid(); - bool result = await this.TechnologyRepository.DoesTechnologyExistAsync(id); + bool result = await this._technologyRepository.DoesTechnologyExistAsync(id); Assert.IsFalse(result, "DoesTechnologyExistAsync returns true when technology does not exist"); } @@ -86,9 +84,9 @@ namespace DevHive.Data.Tests [Test] public async Task DoesTechnologyNameExist_ReturnsTrue_IfTechnologyExists() { - await AddEntity(); + await this.AddEntity(); - bool result = await this.TechnologyRepository.DoesTechnologyNameExistAsync(TECHNOLOGY_NAME); + bool result = await this._technologyRepository.DoesTechnologyNameExistAsync(TECHNOLOGY_NAME); Assert.IsTrue(result, "DoesTechnologyNameExists returns true when technology name does not exist"); } @@ -96,7 +94,7 @@ namespace DevHive.Data.Tests [Test] public async Task DoesTechnologyNameExist_ReturnsFalse_IfTechnologyDoesNotExists() { - bool result = await this.TechnologyRepository.DoesTechnologyNameExistAsync(TECHNOLOGY_NAME); + bool result = await this._technologyRepository.DoesTechnologyNameExistAsync(TECHNOLOGY_NAME); Assert.False(result, "DoesTechnologyNameExistAsync returns true when technology name does not exist"); } @@ -105,13 +103,13 @@ namespace DevHive.Data.Tests #region HelperMethods private async Task AddEntity(string name = TECHNOLOGY_NAME) { - Technology technology = new Technology + Technology technology = new() { Name = name }; - this.Context.Technologies.Add(technology); - await this.Context.SaveChangesAsync(); + this._context.Technologies.Add(technology); + await this._context.SaveChangesAsync(); } #endregion } diff --git a/src/Data/DevHive.Data/ConnectionString.json b/src/Data/DevHive.Data/ConnectionString.json new file mode 100644 index 0000000..ec065d1 --- /dev/null +++ b/src/Data/DevHive.Data/ConnectionString.json @@ -0,0 +1,5 @@ +{ + "ConnectionStrings": { + "DEV": "Server=localhost;Port=5432;Database=DevHive_API;User Id=postgres;Password=;" + } +} diff --git a/src/DevHive.Data/DevHive.Data.csproj b/src/Data/DevHive.Data/DevHive.Data.csproj index d91728d..62320f7 100644 --- a/src/DevHive.Data/DevHive.Data.csproj +++ b/src/Data/DevHive.Data/DevHive.Data.csproj @@ -1,30 +1,24 @@ -<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="AutoMapper" Version="10.1.1" />
- <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" />
-
- <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.1" />
-
- <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.1">
- <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
- <PrivateAssets>all</PrivateAssets>
- </PackageReference>
-
- <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
- </ItemGroup>
-
- <ItemGroup>
- <ProjectReference Include="..\DevHive.Common\DevHive.Common.csproj" />
- </ItemGroup>
-
- <PropertyGroup>
- <EnableNETAnalyzers>true</EnableNETAnalyzers>
- <AnalysisLevel>latest</AnalysisLevel>
- </PropertyGroup>
-
-</Project>
+<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + <ItemGroup> + <PackageReference Include="AutoMapper" Version="10.1.1"/> + <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1"/> + <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="5.0.4"/> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.4"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0"/> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.2"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\DevHive.Data.Models\DevHive.Data.Models.csproj"/> + </ItemGroup> + <PropertyGroup> + <EnableNETAnalyzers>true</EnableNETAnalyzers> + <AnalysisLevel>latest</AnalysisLevel> + </PropertyGroup> +</Project>
\ No newline at end of file diff --git a/src/DevHive.Data/DevHiveContext.cs b/src/Data/DevHive.Data/DevHiveContext.cs index 9de33c3..0606864 100644 --- a/src/DevHive.Data/DevHiveContext.cs +++ b/src/Data/DevHive.Data/DevHiveContext.cs @@ -1,6 +1,6 @@ using System; using DevHive.Data.Models; -using DevHive.Data.RelationModels; +using DevHive.Data.Models.Relational; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; @@ -17,8 +17,7 @@ namespace DevHive.Data public DbSet<PostAttachments> PostAttachments { get; set; } public DbSet<Comment> Comments { get; set; } public DbSet<Rating> Rating { get; set; } - public DbSet<RatedPost> RatedPost { get; set; } - public DbSet<UserRate> UserRate { get; set; } + public DbSet<ProfilePicture> ProfilePicture { get; set; } protected override void OnModelCreating(ModelBuilder builder) { @@ -27,11 +26,6 @@ namespace DevHive.Data .HasIndex(x => x.UserName) .IsUnique(); - builder.Entity<User>() - .HasOne(x => x.ProfilePicture) - .WithOne(x => x.User) - .HasForeignKey<ProfilePicture>(x => x.UserId); - /* Roles */ builder.Entity<User>() .HasMany(x => x.Roles) @@ -88,26 +82,20 @@ namespace DevHive.Data builder.Entity<Rating>() .HasOne(x => x.Post) - .WithOne(x => x.Rating) - .HasForeignKey<Rating>(x => x.PostId); + .WithMany(x => x.Ratings); builder.Entity<Post>() - .HasOne(x => x.Rating) + .HasMany(x => x.Ratings) .WithOne(x => x.Post); - /* User Rated Posts */ - builder.Entity<RatedPost>() - .HasKey(x => new { x.UserId, x.PostId }); - - builder.Entity<RatedPost>() - .HasOne(x => x.User) - .WithMany(x => x.RatedPosts); - - builder.Entity<RatedPost>() - .HasOne(x => x.Post); + /* Profile Picture */ + builder.Entity<ProfilePicture>() + .HasKey(x => x.Id); builder.Entity<User>() - .HasMany(x => x.RatedPosts); + .HasOne(x => x.ProfilePicture) + .WithOne(x => x.User) + .HasForeignKey<ProfilePicture>(x => x.UserId); base.OnModelCreating(builder); } diff --git a/src/DevHive.Data/DevHiveContextFactory.cs b/src/Data/DevHive.Data/DevHiveContextFactory.cs index f4849d7..f4849d7 100644 --- a/src/DevHive.Data/DevHiveContextFactory.cs +++ b/src/Data/DevHive.Data/DevHiveContextFactory.cs diff --git a/src/DevHive.Data/Interfaces/Repositories/ICommentRepository.cs b/src/Data/DevHive.Data/Interfaces/ICommentRepository.cs index 267f251..7b553ec 100644 --- a/src/DevHive.Data/Interfaces/Repositories/ICommentRepository.cs +++ b/src/Data/DevHive.Data/Interfaces/ICommentRepository.cs @@ -2,9 +2,8 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using DevHive.Data.Models; -using DevHive.Data.Repositories.Interfaces; -namespace DevHive.Data.Interfaces.Repositories +namespace DevHive.Data.Interfaces { public interface ICommentRepository : IRepository<Comment> { diff --git a/src/DevHive.Data/Interfaces/Repositories/IFeedRepository.cs b/src/Data/DevHive.Data/Interfaces/IFeedRepository.cs index 7262510..bda51fa 100644 --- a/src/DevHive.Data/Interfaces/Repositories/IFeedRepository.cs +++ b/src/Data/DevHive.Data/Interfaces/IFeedRepository.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using DevHive.Data.Models; -namespace DevHive.Data.Interfaces.Repositories +namespace DevHive.Data.Interfaces { public interface IFeedRepository { diff --git a/src/DevHive.Data/Interfaces/Repositories/ILanguageRepository.cs b/src/Data/DevHive.Data/Interfaces/ILanguageRepository.cs index db2949a..af512c5 100644 --- a/src/DevHive.Data/Interfaces/Repositories/ILanguageRepository.cs +++ b/src/Data/DevHive.Data/Interfaces/ILanguageRepository.cs @@ -2,14 +2,13 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using DevHive.Data.Models; -using DevHive.Data.Repositories.Interfaces; -namespace DevHive.Data.Interfaces.Repositories +namespace DevHive.Data.Interfaces { public interface ILanguageRepository : IRepository<Language> { HashSet<Language> GetLanguages(); - Task<Language> GetByNameAsync(string name); + Task<Language> GetByNameAsync(string languageName); Task<bool> DoesLanguageExistAsync(Guid id); Task<bool> DoesLanguageNameExistAsync(string languageName); diff --git a/src/DevHive.Data/Interfaces/Repositories/IPostRepository.cs b/src/Data/DevHive.Data/Interfaces/IPostRepository.cs index 9f7cf85..d40a487 100644 --- a/src/DevHive.Data/Interfaces/Repositories/IPostRepository.cs +++ b/src/Data/DevHive.Data/Interfaces/IPostRepository.cs @@ -2,15 +2,14 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using DevHive.Data.Models; -using DevHive.Data.Repositories.Interfaces; -namespace DevHive.Data.Interfaces.Repositories +namespace DevHive.Data.Interfaces { public interface IPostRepository : IRepository<Post> { Task<bool> AddNewPostToCreator(Guid userId, Post post); - Task<Post> GetPostByCreatorAndTimeCreatedAsync(Guid issuerId, DateTime timeCreated); + Task<Post> GetPostByCreatorAndTimeCreatedAsync(Guid creatorId, DateTime timeCreated); Task<List<string>> GetFileUrls(Guid postId); Task<bool> DoesPostExist(Guid postId); diff --git a/src/Data/DevHive.Data/Interfaces/IProfilePictureRepository.cs b/src/Data/DevHive.Data/Interfaces/IProfilePictureRepository.cs new file mode 100644 index 0000000..45beaa0 --- /dev/null +++ b/src/Data/DevHive.Data/Interfaces/IProfilePictureRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; +using DevHive.Data.Models; + +namespace DevHive.Data.Interfaces +{ + public interface IProfilePictureRepository : IRepository<ProfilePicture> + { + Task<ProfilePicture> GetByURLAsync(string picUrl); + } +} diff --git a/src/Data/DevHive.Data/Interfaces/IRatingRepository.cs b/src/Data/DevHive.Data/Interfaces/IRatingRepository.cs new file mode 100644 index 0000000..4840c59 --- /dev/null +++ b/src/Data/DevHive.Data/Interfaces/IRatingRepository.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DevHive.Data.Models; +namespace DevHive.Data.Interfaces +{ + public interface IRatingRepository : IRepository<Rating> + { + Task<List<Rating>> GetRatingsByPostId(Guid postId); + Task<bool> UserRatedPost(Guid userId, Guid postId); + Task<Rating> GetRatingByUserAndPostId(Guid userId, Guid postId); + + Task<bool> DoesRatingExist(Guid id); + } +} diff --git a/src/DevHive.Data/Interfaces/Repositories/IRepository.cs b/src/Data/DevHive.Data/Interfaces/IRepository.cs index 0d11cd3..7db8667 100644 --- a/src/DevHive.Data/Interfaces/Repositories/IRepository.cs +++ b/src/Data/DevHive.Data/Interfaces/IRepository.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; -namespace DevHive.Data.Repositories.Interfaces +namespace DevHive.Data.Interfaces { public interface IRepository<TEntity> where TEntity : class diff --git a/src/DevHive.Data/Interfaces/Repositories/IRoleRepository.cs b/src/Data/DevHive.Data/Interfaces/IRoleRepository.cs index e834369..b12772c 100644 --- a/src/DevHive.Data/Interfaces/Repositories/IRoleRepository.cs +++ b/src/Data/DevHive.Data/Interfaces/IRoleRepository.cs @@ -1,9 +1,8 @@ using System; using System.Threading.Tasks; using DevHive.Data.Models; -using DevHive.Data.Repositories.Interfaces; -namespace DevHive.Data.Interfaces.Repositories +namespace DevHive.Data.Interfaces { public interface IRoleRepository : IRepository<Role> { diff --git a/src/DevHive.Data/Interfaces/Repositories/ITechnologyRepository.cs b/src/Data/DevHive.Data/Interfaces/ITechnologyRepository.cs index 9126bfc..91c82ea 100644 --- a/src/DevHive.Data/Interfaces/Repositories/ITechnologyRepository.cs +++ b/src/Data/DevHive.Data/Interfaces/ITechnologyRepository.cs @@ -2,13 +2,12 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using DevHive.Data.Models; -using DevHive.Data.Repositories.Interfaces; -namespace DevHive.Data.Interfaces.Repositories +namespace DevHive.Data.Interfaces { public interface ITechnologyRepository : IRepository<Technology> { - Task<Technology> GetByNameAsync(string name); + Task<Technology> GetByNameAsync(string technologyName); HashSet<Technology> GetTechnologies(); Task<bool> DoesTechnologyExistAsync(Guid id); diff --git a/src/DevHive.Data/Interfaces/Repositories/IUserRepository.cs b/src/Data/DevHive.Data/Interfaces/IUserRepository.cs index 5ebe3d3..494f540 100644 --- a/src/DevHive.Data/Interfaces/Repositories/IUserRepository.cs +++ b/src/Data/DevHive.Data/Interfaces/IUserRepository.cs @@ -2,21 +2,22 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using DevHive.Data.Models; -using DevHive.Data.Repositories.Interfaces; -namespace DevHive.Data.Interfaces.Repositories +namespace DevHive.Data.Interfaces { public interface IUserRepository : IRepository<User> { - //Read + Task<bool> AddRoleToUser(User user, string roleName); + Task<User> GetByUsernameAsync(string username); Task<bool> UpdateProfilePicture(Guid userId, string pictureUrl); - //Validations + Task<bool> VerifyPassword(User user, string password); + Task<bool> IsInRoleAsync(User user, string roleName); Task<bool> ValidateFriendsCollectionAsync(List<string> usernames); Task<bool> DoesEmailExistAsync(string email); Task<bool> DoesUserExistAsync(Guid id); Task<bool> DoesUsernameExistAsync(string username); - bool DoesUserHaveThisUsername(Guid id, string username); + Task<bool> DoesUserHaveThisUsernameAsync(Guid id, string username); } } diff --git a/src/DevHive.Data/Migrations/20210205160520_Friends_First_Tweek.Designer.cs b/src/Data/DevHive.Data/Migrations/20210407161947_Initial_migration.Designer.cs index 3ad3ec8..34c8300 100644 --- a/src/DevHive.Data/Migrations/20210205160520_Friends_First_Tweek.Designer.cs +++ b/src/Data/DevHive.Data/Migrations/20210407161947_Initial_migration.Designer.cs @@ -1,6 +1,5 @@ // <auto-generated /> using System; -using System.Collections.Generic; using DevHive.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -11,16 +10,16 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace DevHive.Data.Migrations { [DbContext(typeof(DevHiveContext))] - [Migration("20210205160520_Friends_First_Tweek")] - partial class Friends_First_Tweek + [Migration("20210407161947_Initial_migration")] + partial class Initial_migration { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .UseIdentityByDefaultColumns() .HasAnnotation("Relational:MaxIdentifierLength", 63) - .HasAnnotation("ProductVersion", "5.0.1"); + .HasAnnotation("ProductVersion", "5.0.4") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); modelBuilder.Entity("DevHive.Data.Models.Comment", b => { @@ -72,9 +71,6 @@ namespace DevHive.Data.Migrations b.Property<Guid?>("CreatorId") .HasColumnType("uuid"); - b.Property<List<string>>("FileUrls") - .HasColumnType("text[]"); - b.Property<string>("Message") .HasColumnType("text"); @@ -105,7 +101,7 @@ namespace DevHive.Data.Migrations b.HasIndex("UserId") .IsUnique(); - b.ToTable("ProfilePicture"); + b.ToTable("ProfilePictures"); }); modelBuilder.Entity("DevHive.Data.Models.Rating", b => @@ -114,14 +110,43 @@ namespace DevHive.Data.Migrations .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property<int>("Rate") - .HasColumnType("integer"); + b.Property<bool>("IsLike") + .HasColumnType("boolean"); + + b.Property<Guid?>("PostId") + .HasColumnType("uuid"); + + b.Property<Guid?>("UserId") + .HasColumnType("uuid"); b.HasKey("Id"); + b.HasIndex("PostId"); + + b.HasIndex("UserId"); + b.ToTable("Rating"); }); + modelBuilder.Entity("DevHive.Data.Models.Relational.PostAttachments", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property<string>("FileUrl") + .HasColumnType("text"); + + b.Property<Guid?>("PostId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.ToTable("PostAttachments"); + }); + modelBuilder.Entity("DevHive.Data.Models.Role", b => { b.Property<Guid>("Id") @@ -242,40 +267,6 @@ namespace DevHive.Data.Migrations b.ToTable("AspNetUsers"); }); - modelBuilder.Entity("DevHive.Data.RelationModels.RatedPost", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("PostId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "PostId"); - - b.HasIndex("PostId"); - - b.ToTable("RatedPosts"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserRate", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<bool>("Rate") - .HasColumnType("boolean"); - - b.Property<Guid?>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserRates"); - }); - modelBuilder.Entity("LanguageUser", b => { b.Property<Guid>("LanguagesId") @@ -296,7 +287,7 @@ namespace DevHive.Data.Migrations b.Property<int>("Id") .ValueGeneratedOnAdd() .HasColumnType("integer") - .UseIdentityByDefaultColumn(); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property<string>("ClaimType") .HasColumnType("text"); @@ -319,7 +310,7 @@ namespace DevHive.Data.Migrations b.Property<int>("Id") .ValueGeneratedOnAdd() .HasColumnType("integer") - .UseIdentityByDefaultColumn(); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property<string>("ClaimType") .HasColumnType("text"); @@ -457,39 +448,35 @@ namespace DevHive.Data.Migrations b.Navigation("User"); }); - modelBuilder.Entity("DevHive.Data.Models.User", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany("Friends") - .HasForeignKey("UserId"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.RatedPost", b => + modelBuilder.Entity("DevHive.Data.Models.Rating", b => { b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany() - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .WithMany("Ratings") + .HasForeignKey("PostId"); b.HasOne("DevHive.Data.Models.User", "User") .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("UserId"); b.Navigation("Post"); b.Navigation("User"); }); - modelBuilder.Entity("DevHive.Data.RelationModels.UserRate", b => + modelBuilder.Entity("DevHive.Data.Models.Relational.PostAttachments", b => { - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); + b.HasOne("DevHive.Data.Models.Post", "Post") + .WithMany("Attachments") + .HasForeignKey("PostId"); - b.Navigation("User"); + b.Navigation("Post"); + }); + + modelBuilder.Entity("DevHive.Data.Models.User", b => + { + b.HasOne("DevHive.Data.Models.User", null) + .WithMany("Friends") + .HasForeignKey("UserId"); }); modelBuilder.Entity("LanguageUser", b => @@ -590,7 +577,11 @@ namespace DevHive.Data.Migrations modelBuilder.Entity("DevHive.Data.Models.Post", b => { + b.Navigation("Attachments"); + b.Navigation("Comments"); + + b.Navigation("Ratings"); }); modelBuilder.Entity("DevHive.Data.Models.User", b => diff --git a/src/DevHive.Data/Migrations/20210205140955_rating.cs b/src/Data/DevHive.Data/Migrations/20210407161947_Initial_migration.cs index d507dae..841c94d 100644 --- a/src/DevHive.Data/Migrations/20210205140955_rating.cs +++ b/src/Data/DevHive.Data/Migrations/20210407161947_Initial_migration.cs @@ -1,11 +1,10 @@ using System; -using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace DevHive.Data.Migrations { - public partial class rating : Migration + public partial class Initial_migration : Migration { protected override void Up(MigrationBuilder migrationBuilder) { @@ -30,6 +29,7 @@ namespace DevHive.Data.Migrations Id = table.Column<Guid>(type: "uuid", nullable: false), FirstName = table.Column<string>(type: "text", nullable: true), LastName = table.Column<string>(type: "text", nullable: true), + UserId = table.Column<Guid>(type: "uuid", nullable: true), UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true), NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true), Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true), @@ -48,6 +48,12 @@ namespace DevHive.Data.Migrations constraints: table => { table.PrimaryKey("PK_AspNetUsers", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUsers_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); }); migrationBuilder.CreateTable( @@ -187,8 +193,7 @@ namespace DevHive.Data.Migrations Id = table.Column<Guid>(type: "uuid", nullable: false), CreatorId = table.Column<Guid>(type: "uuid", nullable: true), Message = table.Column<string>(type: "text", nullable: true), - TimeCreated = table.Column<DateTime>(type: "timestamp without time zone", nullable: false), - FileUrls = table.Column<List<string>>(type: "text[]", nullable: true) + TimeCreated = table.Column<DateTime>(type: "timestamp without time zone", nullable: false) }, constraints: table => { @@ -202,7 +207,7 @@ namespace DevHive.Data.Migrations }); migrationBuilder.CreateTable( - name: "ProfilePicture", + name: "ProfilePictures", columns: table => new { Id = table.Column<Guid>(type: "uuid", nullable: false), @@ -211,9 +216,9 @@ namespace DevHive.Data.Migrations }, constraints: table => { - table.PrimaryKey("PK_ProfilePicture", x => x.Id); + table.PrimaryKey("PK_ProfilePictures", x => x.Id); table.ForeignKey( - name: "FK_ProfilePicture_AspNetUsers_UserId", + name: "FK_ProfilePictures_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", @@ -245,30 +250,6 @@ namespace DevHive.Data.Migrations }); migrationBuilder.CreateTable( - name: "UserFriends", - columns: table => new - { - UserId = table.Column<Guid>(type: "uuid", nullable: false), - FriendId = table.Column<Guid>(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserFriends", x => new { x.UserId, x.FriendId }); - table.ForeignKey( - name: "FK_UserFriends_AspNetUsers_FriendId", - column: x => x.FriendId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_UserFriends_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( name: "LanguageUser", columns: table => new { @@ -344,68 +325,44 @@ namespace DevHive.Data.Migrations }); migrationBuilder.CreateTable( - name: "RatedPosts", - columns: table => new - { - UserId = table.Column<Guid>(type: "uuid", nullable: false), - PostId = table.Column<Guid>(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_RatedPosts", x => new { x.UserId, x.PostId }); - table.ForeignKey( - name: "FK_RatedPosts_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_RatedPosts_Posts_PostId", - column: x => x.PostId, - principalTable: "Posts", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "Rating", + name: "PostAttachments", columns: table => new { Id = table.Column<Guid>(type: "uuid", nullable: false), - PostId = table.Column<Guid>(type: "uuid", nullable: false), - Rate = table.Column<int>(type: "integer", nullable: false) + PostId = table.Column<Guid>(type: "uuid", nullable: true), + FileUrl = table.Column<string>(type: "text", nullable: true) }, constraints: table => { - table.PrimaryKey("PK_Rating", x => x.Id); + table.PrimaryKey("PK_PostAttachments", x => x.Id); table.ForeignKey( - name: "FK_Rating_Posts_PostId", + name: "FK_PostAttachments_Posts_PostId", column: x => x.PostId, principalTable: "Posts", principalColumn: "Id", - onDelete: ReferentialAction.Cascade); + onDelete: ReferentialAction.Restrict); }); migrationBuilder.CreateTable( - name: "UserRates", + name: "Rating", columns: table => new { Id = table.Column<Guid>(type: "uuid", nullable: false), UserId = table.Column<Guid>(type: "uuid", nullable: true), - Liked = table.Column<bool>(type: "boolean", nullable: false), - PostId = table.Column<Guid>(type: "uuid", nullable: true) + PostId = table.Column<Guid>(type: "uuid", nullable: true), + IsLike = table.Column<bool>(type: "boolean", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_UserRates", x => x.Id); + table.PrimaryKey("PK_Rating", x => x.Id); table.ForeignKey( - name: "FK_UserRates_AspNetUsers_UserId", + name: "FK_Rating_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Restrict); table.ForeignKey( - name: "FK_UserRates_Posts_PostId", + name: "FK_Rating_Posts_PostId", column: x => x.PostId, principalTable: "Posts", principalColumn: "Id", @@ -444,6 +401,11 @@ namespace DevHive.Data.Migrations column: "NormalizedEmail"); migrationBuilder.CreateIndex( + name: "IX_AspNetUsers_UserId", + table: "AspNetUsers", + column: "UserId"); + + migrationBuilder.CreateIndex( name: "IX_AspNetUsers_UserName", table: "AspNetUsers", column: "UserName", @@ -471,26 +433,30 @@ namespace DevHive.Data.Migrations column: "UsersId"); migrationBuilder.CreateIndex( + name: "IX_PostAttachments_PostId", + table: "PostAttachments", + column: "PostId"); + + migrationBuilder.CreateIndex( name: "IX_Posts_CreatorId", table: "Posts", column: "CreatorId"); migrationBuilder.CreateIndex( - name: "IX_ProfilePicture_UserId", - table: "ProfilePicture", + name: "IX_ProfilePictures_UserId", + table: "ProfilePictures", column: "UserId", unique: true); migrationBuilder.CreateIndex( - name: "IX_RatedPosts_PostId", - table: "RatedPosts", + name: "IX_Rating_PostId", + table: "Rating", column: "PostId"); migrationBuilder.CreateIndex( - name: "IX_Rating_PostId", + name: "IX_Rating_UserId", table: "Rating", - column: "PostId", - unique: true); + column: "UserId"); migrationBuilder.CreateIndex( name: "IX_RoleUser_UsersId", @@ -501,21 +467,6 @@ namespace DevHive.Data.Migrations name: "IX_TechnologyUser_UsersId", table: "TechnologyUser", column: "UsersId"); - - migrationBuilder.CreateIndex( - name: "IX_UserFriends_FriendId", - table: "UserFriends", - column: "FriendId"); - - migrationBuilder.CreateIndex( - name: "IX_UserRates_PostId", - table: "UserRates", - column: "PostId"); - - migrationBuilder.CreateIndex( - name: "IX_UserRates_UserId", - table: "UserRates", - column: "UserId"); } protected override void Down(MigrationBuilder migrationBuilder) @@ -542,10 +493,10 @@ namespace DevHive.Data.Migrations name: "LanguageUser"); migrationBuilder.DropTable( - name: "ProfilePicture"); + name: "PostAttachments"); migrationBuilder.DropTable( - name: "RatedPosts"); + name: "ProfilePictures"); migrationBuilder.DropTable( name: "Rating"); @@ -557,13 +508,10 @@ namespace DevHive.Data.Migrations name: "TechnologyUser"); migrationBuilder.DropTable( - name: "UserFriends"); - - migrationBuilder.DropTable( - name: "UserRates"); + name: "Languages"); migrationBuilder.DropTable( - name: "Languages"); + name: "Posts"); migrationBuilder.DropTable( name: "AspNetRoles"); @@ -572,9 +520,6 @@ namespace DevHive.Data.Migrations name: "Technologies"); migrationBuilder.DropTable( - name: "Posts"); - - migrationBuilder.DropTable( name: "AspNetUsers"); } } diff --git a/src/DevHive.Data/Migrations/DevHiveContextModelSnapshot.cs b/src/Data/DevHive.Data/Migrations/DevHiveContextModelSnapshot.cs index dc5739c..c451979 100644 --- a/src/DevHive.Data/Migrations/DevHiveContextModelSnapshot.cs +++ b/src/Data/DevHive.Data/Migrations/DevHiveContextModelSnapshot.cs @@ -15,9 +15,9 @@ namespace DevHive.Data.Migrations { #pragma warning disable 612, 618 modelBuilder - .UseIdentityByDefaultColumns() .HasAnnotation("Relational:MaxIdentifierLength", 63) - .HasAnnotation("ProductVersion", "5.0.1"); + .HasAnnotation("ProductVersion", "5.0.4") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); modelBuilder.Entity("DevHive.Data.Models.Comment", b => { @@ -99,7 +99,7 @@ namespace DevHive.Data.Migrations b.HasIndex("UserId") .IsUnique(); - b.ToTable("ProfilePicture"); + b.ToTable("ProfilePictures"); }); modelBuilder.Entity("DevHive.Data.Models.Rating", b => @@ -108,20 +108,43 @@ namespace DevHive.Data.Migrations .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property<Guid>("PostId") + b.Property<bool>("IsLike") + .HasColumnType("boolean"); + + b.Property<Guid?>("PostId") .HasColumnType("uuid"); - b.Property<int>("Rate") - .HasColumnType("integer"); + b.Property<Guid?>("UserId") + .HasColumnType("uuid"); b.HasKey("Id"); - b.HasIndex("PostId") - .IsUnique(); + b.HasIndex("PostId"); + + b.HasIndex("UserId"); b.ToTable("Rating"); }); + modelBuilder.Entity("DevHive.Data.Models.Relational.PostAttachments", b => + { + b.Property<Guid>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property<string>("FileUrl") + .HasColumnType("text"); + + b.Property<Guid?>("PostId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PostId"); + + b.ToTable("PostAttachments"); + }); + modelBuilder.Entity("DevHive.Data.Models.Role", b => { b.Property<Guid>("Id") @@ -242,64 +265,6 @@ namespace DevHive.Data.Migrations b.ToTable("AspNetUsers"); }); - modelBuilder.Entity("DevHive.Data.RelationModels.PostAttachments", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("FileUrl") - .HasColumnType("text"); - - b.Property<Guid?>("PostId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("PostId"); - - b.ToTable("PostAttachments"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.RatedPost", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("PostId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "PostId"); - - b.HasIndex("PostId"); - - b.ToTable("RatedPosts"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserRate", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<bool>("Liked") - .HasColumnType("boolean"); - - b.Property<Guid?>("PostId") - .HasColumnType("uuid"); - - b.Property<Guid?>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("PostId"); - - b.HasIndex("UserId"); - - b.ToTable("UserRates"); - }); - modelBuilder.Entity("LanguageUser", b => { b.Property<Guid>("LanguagesId") @@ -320,7 +285,7 @@ namespace DevHive.Data.Migrations b.Property<int>("Id") .ValueGeneratedOnAdd() .HasColumnType("integer") - .UseIdentityByDefaultColumn(); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property<string>("ClaimType") .HasColumnType("text"); @@ -343,7 +308,7 @@ namespace DevHive.Data.Migrations b.Property<int>("Id") .ValueGeneratedOnAdd() .HasColumnType("integer") - .UseIdentityByDefaultColumn(); + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); b.Property<string>("ClaimType") .HasColumnType("text"); @@ -484,55 +449,32 @@ namespace DevHive.Data.Migrations modelBuilder.Entity("DevHive.Data.Models.Rating", b => { b.HasOne("DevHive.Data.Models.Post", "Post") - .WithOne("Rating") - .HasForeignKey("DevHive.Data.Models.Rating", "PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.PostAttachments", b => - { - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany("Attachments") + .WithMany("Ratings") .HasForeignKey("PostId"); - b.Navigation("Post"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.RatedPost", b => - { - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany() - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany("RatedPosts") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .WithMany() + .HasForeignKey("UserId"); b.Navigation("Post"); b.Navigation("User"); }); - modelBuilder.Entity("DevHive.Data.RelationModels.UserRate", b => + modelBuilder.Entity("DevHive.Data.Models.Relational.PostAttachments", b => { b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany() + .WithMany("Attachments") .HasForeignKey("PostId"); - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - b.Navigation("Post"); + }); - b.Navigation("User"); + modelBuilder.Entity("DevHive.Data.Models.User", b => + { + b.HasOne("DevHive.Data.Models.User", null) + .WithMany("Friends") + .HasForeignKey("UserId"); }); modelBuilder.Entity("LanguageUser", b => @@ -637,7 +579,7 @@ namespace DevHive.Data.Migrations b.Navigation("Comments"); - b.Navigation("Rating"); + b.Navigation("Ratings"); }); modelBuilder.Entity("DevHive.Data.Models.User", b => @@ -649,8 +591,6 @@ namespace DevHive.Data.Migrations b.Navigation("Posts"); b.Navigation("ProfilePicture"); - - b.Navigation("RatedPosts"); }); #pragma warning restore 612, 618 } diff --git a/src/DevHive.Data/Repositories/BaseRepository.cs b/src/Data/DevHive.Data/Repositories/BaseRepository.cs index ece372e..239a3bc 100644 --- a/src/DevHive.Data/Repositories/BaseRepository.cs +++ b/src/Data/DevHive.Data/Repositories/BaseRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using DevHive.Data.Repositories.Interfaces; +using DevHive.Data.Interfaces; using Microsoft.EntityFrameworkCore; namespace DevHive.Data.Repositories diff --git a/src/DevHive.Data/Repositories/CommentRepository.cs b/src/Data/DevHive.Data/Repositories/CommentRepository.cs index bee7624..9364776 100644 --- a/src/DevHive.Data/Repositories/CommentRepository.cs +++ b/src/Data/DevHive.Data/Repositories/CommentRepository.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Threading.Tasks; -using DevHive.Data.Interfaces.Repositories; +using DevHive.Data.Interfaces; using DevHive.Data.Models; using Microsoft.EntityFrameworkCore; @@ -29,8 +28,8 @@ namespace DevHive.Data.Repositories } /// <summary> - /// This method returns the comment that is made at exactly the given time and by the given creator - /// </summary> + /// This method returns the comment that is made at exactly the given time and by the given creator + /// </summary> public async Task<Comment> GetCommentByIssuerAndTimeCreatedAsync(Guid issuerId, DateTime timeCreated) { return await this._context.Comments diff --git a/src/DevHive.Data/Repositories/FeedRepository.cs b/src/Data/DevHive.Data/Repositories/FeedRepository.cs index 8d3e5e1..d3312d7 100644 --- a/src/DevHive.Data/Repositories/FeedRepository.cs +++ b/src/Data/DevHive.Data/Repositories/FeedRepository.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AutoMapper.Internal; -using DevHive.Data.Interfaces.Repositories; +using DevHive.Data.Interfaces; using DevHive.Data.Models; using Microsoft.EntityFrameworkCore; @@ -19,14 +19,14 @@ namespace DevHive.Data.Repositories } /// <summary> - /// This returns a given amount of posts of all given friends, created before "firstRequestIssued", + /// This returns a given amount of posts of all given friends, created before "firstRequestIssued", /// ordered from latest to oldest (time created). /// PageSize specifies how many posts to get, and pageNumber specifices how many posts to skip (pageNumber * pageSize). /// /// This method is used in the feed page. /// Posts from friends are meant to be gotten in chunks, meaning you get X posts, and then get another amount of posts, /// that are after the first X posts. - /// </summary> + /// </summary> public async Task<List<Post>> GetFriendsPosts(List<User> friendsList, DateTime firstRequestIssued, int pageNumber, int pageSize) { List<Guid> friendsIds = friendsList.Select(f => f.Id).ToList(); @@ -49,14 +49,14 @@ namespace DevHive.Data.Repositories } /// <summary> - /// This returns a given amount of posts, that a user has made, created before "firstRequestIssued", + /// This returns a given amount of posts, that a user has made, created before "firstRequestIssued", /// ordered from latest to oldest (time created). /// PageSize specifies how many posts to get, and pageNumber specifices how many posts to skip (pageNumber * pageSize). /// /// This method is used in the profile page. /// Posts from friends are meant to be gotten in chunks, meaning you get X posts, and then get another amount of posts, /// that are after the first X posts. - /// </summary> + /// </summary> public async Task<List<Post>> GetUsersPosts(User user, DateTime firstRequestIssued, int pageNumber, int pageSize) { List<Post> posts = await this._context.Posts diff --git a/src/DevHive.Data/Repositories/LanguageRepository.cs b/src/Data/DevHive.Data/Repositories/LanguageRepository.cs index 31d0b86..3528ea8 100644 --- a/src/DevHive.Data/Repositories/LanguageRepository.cs +++ b/src/Data/DevHive.Data/Repositories/LanguageRepository.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using DevHive.Data.Interfaces.Repositories; +using DevHive.Data.Interfaces; using DevHive.Data.Models; using Microsoft.EntityFrameworkCore; @@ -26,8 +26,8 @@ namespace DevHive.Data.Repositories } /// <summary> - /// Returns all technologies that exist in the database - /// </summary> + /// Returns all technologies that exist in the database + /// </summary> public HashSet<Language> GetLanguages() { return this._context.Languages.ToHashSet(); diff --git a/src/DevHive.Data/Repositories/PostRepository.cs b/src/Data/DevHive.Data/Repositories/PostRepository.cs index 52c5b4e..b5228c2 100644 --- a/src/DevHive.Data/Repositories/PostRepository.cs +++ b/src/Data/DevHive.Data/Repositories/PostRepository.cs @@ -2,14 +2,14 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using DevHive.Data.Interfaces.Repositories; +using DevHive.Data.Interfaces; using DevHive.Data.Models; -using DevHive.Data.RelationModels; +using DevHive.Data.Models.Relational; using Microsoft.EntityFrameworkCore; namespace DevHive.Data.Repositories { - public class PostRepository : BaseRepository<Post>, IPostRepository + public class PostRepository : BaseRepository<Post>, IPostRepository { private readonly DevHiveContext _context; private readonly IUserRepository _userRepository; @@ -36,13 +36,13 @@ namespace DevHive.Data.Repositories .Include(x => x.Comments) .Include(x => x.Creator) .Include(x => x.Attachments) - // .Include(x => x.Rating) + .Include(x => x.Ratings) .FirstOrDefaultAsync(x => x.Id == id); } /// <summary> - /// This method returns the post that is made at exactly the given time and by the given creator - /// </summary> + /// This method returns the post that is made at exactly the given time and by the given creator + /// </summary> public async Task<Post> GetPostByCreatorAndTimeCreatedAsync(Guid creatorId, DateTime timeCreated) { return await this._context.Posts @@ -60,7 +60,6 @@ namespace DevHive.Data.Repositories public override async Task<bool> EditAsync(Guid id, Post newEntity) { Post post = await this.GetByIdAsync(id); - // var ratingId = post.Rating.Id; this._context .Entry(post) @@ -68,16 +67,14 @@ namespace DevHive.Data.Repositories .SetValues(newEntity); List<PostAttachments> postAttachments = new(); - foreach(var attachment in newEntity.Attachments) + foreach (var attachment in newEntity.Attachments) postAttachments.Add(attachment); post.Attachments = postAttachments; post.Comments.Clear(); - foreach(var comment in newEntity.Comments) + foreach (var comment in newEntity.Comments) post.Comments.Add(comment); - // post.Rating.Id = ratingId; - this._context.Entry(post).State = EntityState.Modified; return await this.SaveChangesAsync(); diff --git a/src/Data/DevHive.Data/Repositories/ProfilePictureRepository.cs b/src/Data/DevHive.Data/Repositories/ProfilePictureRepository.cs new file mode 100644 index 0000000..7284464 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/ProfilePictureRepository.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class ProfilePictureRepository : BaseRepository<ProfilePicture>, IProfilePictureRepository + { + private readonly DevHiveContext _context; + + public ProfilePictureRepository(DevHiveContext context) + : base(context) + { + this._context = context; + } + + public async Task<ProfilePicture> GetByURLAsync(string picUrl) + { + return await this._context + .ProfilePicture + .FirstOrDefaultAsync(x => x.PictureURL == picUrl); + } + } +} diff --git a/src/Data/DevHive.Data/Repositories/RatingRepository.cs b/src/Data/DevHive.Data/Repositories/RatingRepository.cs new file mode 100644 index 0000000..2048c3f --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/RatingRepository.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class RatingRepository : BaseRepository<Rating>, IRatingRepository + { + private readonly DevHiveContext _context; + + public RatingRepository(DevHiveContext context) + : base(context) + { + this._context = context; + } + + public override async Task<Rating> GetByIdAsync(Guid id) + { + return await this._context.Rating + .Include(x => x.User) + .Include(x => x.Post) + .FirstOrDefaultAsync(x => x.Id == id); + } + /// <summary> + /// Gets all the ratings for a post. + /// </summary> + /// <param name="postId">Id of the post.</param> + /// <returns></returns> + public async Task<List<Rating>> GetRatingsByPostId(Guid postId) + { + return await this._context.Rating + .Include(x => x.User) + .Include(x => x.Post) + .Where(x => x.Post.Id == postId).ToListAsync(); + } + /// <summary> + /// Checks if a user rated a given post. In DevHive every user has one or no rating for every post. + /// </summary> + /// <param name="userId">Id of the user.</param> + /// <param name="postId">Id of the post.</param> + /// <returns>True if the user has already rated the post and false if he hasn't.</returns> + public async Task<bool> UserRatedPost(Guid userId, Guid postId) + { + return await this._context.Rating + .Where(x => x.Post.Id == postId) + .AnyAsync(x => x.User.Id == userId); + } + /// <summary> + /// Gets a rating by the post to which the rating corresponds and the user who created it. + /// </summary> + /// <param name="userId">Id of the user.</param> + /// <param name="postId">Id of the post.</param> + /// <returns>Rating for the given post by the given user.</returns> + public async Task<Rating> GetRatingByUserAndPostId(Guid userId, Guid postId) + { + return await this._context.Rating + .Include(x => x.User) + .Include(x => x.Post) + .FirstOrDefaultAsync(x => x.Post.Id == postId && x.User.Id == userId); + } + + /// <summary> + /// Checks if a given rating already exist + /// </summary> + /// <param name="id">Id of the rating</param> + /// <returns>True if the rating exists and false if it does not.</returns> + public async Task<bool> DoesRatingExist(Guid id) + { + return await this._context.Rating + .AsNoTracking() + .AnyAsync(r => r.Id == id); + } + } +} + diff --git a/src/Data/DevHive.Data/Repositories/RoleRepository.cs b/src/Data/DevHive.Data/Repositories/RoleRepository.cs new file mode 100644 index 0000000..99e0634 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/RoleRepository.cs @@ -0,0 +1,56 @@ +using System; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class RoleRepository : BaseRepository<Role>, IRoleRepository + { + private readonly RoleManager<Role> _roleManager; + + public RoleRepository(DevHiveContext context, RoleManager<Role> roleManager) + : base(context) + { + this._roleManager = roleManager; + } + + #region Create + public override async Task<bool> AddAsync(Role entity) + { + IdentityResult result = await this._roleManager.CreateAsync(entity); + + return result.Succeeded; + } + #endregion + + #region Read + public async Task<Role> GetByNameAsync(string name) + { + return await this._roleManager.FindByNameAsync(name); + } + #endregion + + public override async Task<bool> EditAsync(Guid id, Role newEntity) + { + newEntity.Id = id; + IdentityResult result = await this._roleManager.UpdateAsync(newEntity); + + return result.Succeeded; + } + + #region Validations + public async Task<bool> DoesNameExist(string name) + { + return await this._roleManager.RoleExistsAsync(name); + } + + public async Task<bool> DoesRoleExist(Guid id) + { + return await this._roleManager.Roles.AnyAsync(r => r.Id == id); + } + #endregion + } +} diff --git a/src/DevHive.Data/Repositories/TechnologyRepository.cs b/src/Data/DevHive.Data/Repositories/TechnologyRepository.cs index 6f0d10f..d0d1f3f 100644 --- a/src/DevHive.Data/Repositories/TechnologyRepository.cs +++ b/src/Data/DevHive.Data/Repositories/TechnologyRepository.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using DevHive.Data.Interfaces.Repositories; +using DevHive.Data.Interfaces; using DevHive.Data.Models; using Microsoft.EntityFrameworkCore; @@ -26,8 +26,8 @@ namespace DevHive.Data.Repositories } /// <summary> - /// Returns all technologies that exist in the database - /// </summary> + /// Returns all technologies that exist in the database + /// </summary> public HashSet<Technology> GetTechnologies() { return this._context.Technologies.ToHashSet(); diff --git a/src/Data/DevHive.Data/Repositories/UserRepository.cs b/src/Data/DevHive.Data/Repositories/UserRepository.cs new file mode 100644 index 0000000..d570480 --- /dev/null +++ b/src/Data/DevHive.Data/Repositories/UserRepository.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; + +namespace DevHive.Data.Repositories +{ + public class UserRepository : BaseRepository<User>, IUserRepository + { + private readonly UserManager<User> _userManager; + private readonly RoleManager<Role> _roleManager; + + public UserRepository(DevHiveContext context, UserManager<User> userManager, RoleManager<Role> roleManager) + : base(context) + { + this._userManager = userManager; + this._roleManager = roleManager; + } + + #region Create + public override async Task<bool> AddAsync(User entity) + { + entity.PasswordHash = this._userManager.PasswordHasher.HashPassword(entity, entity.PasswordHash).ToString(); + IdentityResult result = await this._userManager.CreateAsync(entity); + + return result.Succeeded; + } + + public async Task<bool> AddRoleToUser(User user, string roleName) + { + bool succeeded = (await this._userManager.AddToRoleAsync(user, roleName)).Succeeded; + if (succeeded) + { + user.Roles.Add(await this._roleManager.FindByNameAsync(roleName)); + succeeded = await this.SaveChangesAsync(); + } + + return succeeded; + } + #endregion + + #region Read + public override async Task<User> GetByIdAsync(Guid id) + { + return await this._userManager.Users + .Include(x => x.Roles) + .Include(x => x.Languages) + .Include(x => x.Technologies) + .Include(x => x.Posts) + .Include(x => x.Friends) + .Include(x => x.ProfilePicture) + .FirstOrDefaultAsync(x => x.Id == id); + } + + public async Task<User> GetByUsernameAsync(string username) + { + return await this._userManager.Users + .Include(x => x.Roles) + .Include(x => x.Languages) + .Include(x => x.Technologies) + .Include(x => x.Posts) + .Include(x => x.Friends) + .Include(x => x.ProfilePicture) + .FirstOrDefaultAsync(x => x.UserName == username); + } + #endregion + + #region Update + public override async Task<bool> EditAsync(Guid id, User newEntity) + { + newEntity.Id = id; + IdentityResult result = await this._userManager.UpdateAsync(newEntity); + + return result.Succeeded; + } + + public async Task<bool> UpdateProfilePicture(Guid userId, string pictureUrl) + { + User user = await this.GetByIdAsync(userId); + + user.ProfilePicture.PictureURL = pictureUrl; + + return await this.SaveChangesAsync(); + } + #endregion + + #region Delete + public override async Task<bool> DeleteAsync(User entity) + { + IdentityResult result = await this._userManager.DeleteAsync(entity); + + return result.Succeeded; + } + #endregion + + #region Validations + public async Task<bool> VerifyPassword(User user, string password) + { + return await this._userManager.CheckPasswordAsync(user, password); + } + + public async Task<bool> IsInRoleAsync(User user, string roleName) + { + return await this._userManager.IsInRoleAsync(user, roleName); + } + + public async Task<bool> DoesUserExistAsync(Guid id) + { + return await this._userManager.Users.AnyAsync(x => x.Id == id); + } + + public async Task<bool> DoesUsernameExistAsync(string username) + { + return await this._userManager.Users + .AsNoTracking() + .AnyAsync(u => u.UserName == username); + } + + public async Task<bool> DoesEmailExistAsync(string email) + { + return await this._userManager.Users + .AsNoTracking() + .AnyAsync(u => u.Email == email); + } + + public async Task<bool> ValidateFriendsCollectionAsync(List<string> usernames) + { + bool valid = true; + + foreach (var username in usernames) + { + if (!await this.DoesUsernameExistAsync(username)) + { + valid = false; + break; + } + } + return valid; + } + + public async Task<bool> DoesUserHaveThisUsernameAsync(Guid id, string username) + { + return await this._userManager.Users + .AnyAsync(x => x.Id == id && + x.UserName == username); + } + #endregion + } +} diff --git a/src/DevHive.Angular/.browserslistrc b/src/DevHive.Angular/.browserslistrc deleted file mode 100644 index 427441d..0000000 --- a/src/DevHive.Angular/.browserslistrc +++ /dev/null @@ -1,17 +0,0 @@ -# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries - -# For the full list of supported browsers by the Angular framework, please see: -# https://angular.io/guide/browser-support - -# You can see what browsers were selected by your queries by running: -# npx browserslist - -last 1 Chrome version -last 1 Firefox version -last 2 Edge major versions -last 2 Safari major versions -last 2 iOS major versions -Firefox ESR -not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. diff --git a/src/DevHive.Angular/.gitignore b/src/DevHive.Angular/.gitignore deleted file mode 100644 index 86d943a..0000000 --- a/src/DevHive.Angular/.gitignore +++ /dev/null @@ -1,46 +0,0 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. - -# compiled output -/dist -/tmp -/out-tsc -# Only exists if Bazel was run -/bazel-out - -# dependencies -/node_modules - -# profiling files -chrome-profiler-events*.json -speed-measure-plugin*.json - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history/* - -# misc -/.sass-cache -/connect.lock -/coverage -/libpeerconnection.log -npm-debug.log -yarn-error.log -testem.log -/typings - -# System Files -.DS_Store -Thumbs.db diff --git a/src/DevHive.Angular/README.md b/src/DevHive.Angular/README.md deleted file mode 100644 index 8ebb62a..0000000 --- a/src/DevHive.Angular/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Angular - -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.0.5. - -## Development server - -Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. - -## Code scaffolding - -Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. - -## Build - -Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. - -## Running unit tests - -Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). - -## Running end-to-end tests - -Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). - -## Further help - -To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. diff --git a/src/DevHive.Angular/angular.json b/src/DevHive.Angular/angular.json deleted file mode 100644 index 2f7de40..0000000 --- a/src/DevHive.Angular/angular.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "Angular": { - "projectType": "application", - "schematics": { - "@schematics/angular:application": { - "strict": true - } - }, - "root": "", - "sourceRoot": "src", - "prefix": "app", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:browser", - "options": { - "outputPath": "dist/Angular", - "index": "src/index.html", - "main": "src/main.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "tsconfig.app.json", - "aot": true, - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", - "src/styles.css", - "src/theme.scss" - ], - "scripts": [] - }, - "configurations": { - "production": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" - } - ] - } - } - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "options": { - "browserTarget": "Angular:build" - }, - "configurations": { - "production": { - "browserTarget": "Angular:build:production" - } - } - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", - "options": { - "browserTarget": "Angular:build" - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "src/test.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "tsconfig.spec.json", - "karmaConfig": "karma.conf.js", - "assets": [ - "src/favicon.ico", - "src/assets" - ], - "styles": [ - "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", - "src/styles.css" - ], - "scripts": [] - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "tsconfig.app.json", - "tsconfig.spec.json", - "e2e/tsconfig.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - }, - "e2e": { - "builder": "@angular-devkit/build-angular:protractor", - "options": { - "protractorConfig": "e2e/protractor.conf.js", - "devServerTarget": "Angular:serve" - }, - "configurations": { - "production": { - "devServerTarget": "Angular:serve:production" - } - } - } - } - } - }, - "defaultProject": "Angular" -} diff --git a/src/DevHive.Angular/e2e/protractor.conf.js b/src/DevHive.Angular/e2e/protractor.conf.js deleted file mode 100644 index 361e7f0..0000000 --- a/src/DevHive.Angular/e2e/protractor.conf.js +++ /dev/null @@ -1,37 +0,0 @@ -// @ts-check -// Protractor configuration file, see link for more information -// https://github.com/angular/protractor/blob/master/lib/config.ts - -const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); - -/** - * @type { import("protractor").Config } - */ -exports.config = { - allScriptsTimeout: 11000, - specs: [ - './src/**/*.e2e-spec.ts' - ], - capabilities: { - browserName: 'chrome' - }, - directConnect: true, - SELENIUM_PROMISE_MANAGER: false, - baseUrl: 'http://localhost:4200/', - framework: 'jasmine', - jasmineNodeOpts: { - showColors: true, - defaultTimeoutInterval: 30000, - print: function() {} - }, - onPrepare() { - require('ts-node').register({ - project: require('path').join(__dirname, './tsconfig.json') - }); - jasmine.getEnv().addReporter(new SpecReporter({ - spec: { - displayStacktrace: StacktraceOption.PRETTY - } - })); - } -};
\ No newline at end of file diff --git a/src/DevHive.Angular/e2e/src/app.e2e-spec.ts b/src/DevHive.Angular/e2e/src/app.e2e-spec.ts deleted file mode 100644 index 359bbf9..0000000 --- a/src/DevHive.Angular/e2e/src/app.e2e-spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { AppPage } from './app.po'; -import { browser, logging } from 'protractor'; - -describe('workspace-project App', () => { - let page: AppPage; - - beforeEach(() => { - page = new AppPage(); - }); - - it('should display welcome message', async () => { - await page.navigateTo(); - expect(await page.getTitleText()).toEqual('Angular app is running!'); - }); - - afterEach(async () => { - // Assert that there are no errors emitted from the browser - const logs = await browser.manage().logs().get(logging.Type.BROWSER); - expect(logs).not.toContain(jasmine.objectContaining({ - level: logging.Level.SEVERE, - } as logging.Entry)); - }); -}); diff --git a/src/DevHive.Angular/e2e/src/app.po.ts b/src/DevHive.Angular/e2e/src/app.po.ts deleted file mode 100644 index c9c85ab..0000000 --- a/src/DevHive.Angular/e2e/src/app.po.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { browser, by, element } from 'protractor'; - -export class AppPage { - async navigateTo(): Promise<unknown> { - return browser.get(browser.baseUrl); - } - - async getTitleText(): Promise<string> { - return element(by.css('app-root .content span')).getText(); - } -} diff --git a/src/DevHive.Angular/e2e/tsconfig.json b/src/DevHive.Angular/e2e/tsconfig.json deleted file mode 100644 index 0782539..0000000 --- a/src/DevHive.Angular/e2e/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/e2e", - "module": "commonjs", - "target": "es2018", - "types": [ - "jasmine", - "node" - ] - } -} diff --git a/src/DevHive.Angular/karma.conf.js b/src/DevHive.Angular/karma.conf.js deleted file mode 100644 index 65c822a..0000000 --- a/src/DevHive.Angular/karma.conf.js +++ /dev/null @@ -1,44 +0,0 @@ -// Karma configuration file, see link for more information -// https://karma-runner.github.io/1.0/config/configuration-file.html - -module.exports = function (config) { - config.set({ - basePath: '', - frameworks: ['jasmine', '@angular-devkit/build-angular'], - plugins: [ - require('karma-jasmine'), - require('karma-chrome-launcher'), - require('karma-jasmine-html-reporter'), - require('karma-coverage'), - require('@angular-devkit/build-angular/plugins/karma') - ], - client: { - jasmine: { - // you can add configuration options for Jasmine here - // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html - // for example, you can disable the random execution with `random: false` - // or set a specific seed with `seed: 4321` - }, - clearContext: false // leave Jasmine Spec Runner output visible in browser - }, - jasmineHtmlReporter: { - suppressAll: true // removes the duplicated traces - }, - coverageReporter: { - dir: require('path').join(__dirname, './coverage/Angular'), - subdir: '.', - reporters: [ - { type: 'html' }, - { type: 'text-summary' } - ] - }, - reporters: ['progress', 'kjhtml'], - port: 9876, - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - browsers: ['Chrome'], - singleRun: false, - restartOnFileChange: true - }); -}; diff --git a/src/DevHive.Angular/package-lock.json b/src/DevHive.Angular/package-lock.json deleted file mode 100644 index 166d493..0000000 --- a/src/DevHive.Angular/package-lock.json +++ /dev/null @@ -1,13593 +0,0 @@ -{ - "name": "angular", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@angular-devkit/architect": { - "version": "0.1100.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1100.6.tgz", - "integrity": "sha512-4O+cg3AimI2bNAxxdu5NrqSf4Oa8r8xL0+G2Ycd3jLoFv0h0ecJiNKEG5F6IpTprb4aexZD6pcxBJCqQ8MmzWQ==", - "dev": true, - "requires": { - "@angular-devkit/core": "11.0.6", - "rxjs": "6.6.3" - } - }, - "@angular-devkit/build-angular": { - "version": "0.1100.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1100.6.tgz", - "integrity": "sha512-HcqsWiSIUxExGg3HRQScLOmF+ckVkCKolfpPcNOCCpBYxH/i8n4wDGLBP5Rtxky+0Qz+3nnAaFIpNb9p9aUmbg==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.1100.6", - "@angular-devkit/build-optimizer": "0.1100.6", - "@angular-devkit/build-webpack": "0.1100.6", - "@angular-devkit/core": "11.0.6", - "@babel/core": "7.12.3", - "@babel/generator": "7.12.1", - "@babel/plugin-transform-runtime": "7.12.1", - "@babel/preset-env": "7.12.1", - "@babel/runtime": "7.12.1", - "@babel/template": "7.10.4", - "@jsdevtools/coverage-istanbul-loader": "3.0.5", - "@ngtools/webpack": "11.0.6", - "ansi-colors": "4.1.1", - "autoprefixer": "9.8.6", - "babel-loader": "8.1.0", - "browserslist": "^4.9.1", - "cacache": "15.0.5", - "caniuse-lite": "^1.0.30001032", - "circular-dependency-plugin": "5.2.0", - "copy-webpack-plugin": "6.2.1", - "core-js": "3.6.5", - "css-loader": "4.3.0", - "cssnano": "4.1.10", - "file-loader": "6.1.1", - "find-cache-dir": "3.3.1", - "glob": "7.1.6", - "inquirer": "7.3.3", - "jest-worker": "26.5.0", - "karma-source-map-support": "1.4.0", - "less": "3.12.2", - "less-loader": "7.0.2", - "license-webpack-plugin": "2.3.1", - "loader-utils": "2.0.0", - "mini-css-extract-plugin": "1.2.1", - "minimatch": "3.0.4", - "open": "7.3.0", - "ora": "5.1.0", - "parse5-html-rewriting-stream": "6.0.1", - "pnp-webpack-plugin": "1.6.4", - "postcss": "7.0.32", - "postcss-import": "12.0.1", - "postcss-loader": "4.0.4", - "raw-loader": "4.0.2", - "regenerator-runtime": "0.13.7", - "resolve-url-loader": "3.1.2", - "rimraf": "3.0.2", - "rollup": "2.32.1", - "rxjs": "6.6.3", - "sass": "1.27.0", - "sass-loader": "10.0.5", - "semver": "7.3.2", - "source-map": "0.7.3", - "source-map-loader": "1.1.2", - "source-map-support": "0.5.19", - "speed-measure-webpack-plugin": "1.3.3", - "style-loader": "2.0.0", - "stylus": "0.54.8", - "stylus-loader": "4.3.1", - "terser": "5.3.7", - "terser-webpack-plugin": "4.2.3", - "text-table": "0.2.0", - "tree-kill": "1.2.2", - "webpack": "4.44.2", - "webpack-dev-middleware": "3.7.2", - "webpack-dev-server": "3.11.0", - "webpack-merge": "5.2.0", - "webpack-sources": "2.0.1", - "webpack-subresource-integrity": "1.5.1", - "worker-plugin": "5.0.0" - } - }, - "@angular-devkit/build-optimizer": { - "version": "0.1100.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.6.tgz", - "integrity": "sha512-Qkq7n6510N+nXmfZqpqpI0I6Td+b+06RRNmS7KftSNJntU1z5QYh4FggwlthZ5P0QUT92cnBQsnT8OgYqGnwbg==", - "dev": true, - "requires": { - "loader-utils": "2.0.0", - "source-map": "0.7.3", - "tslib": "2.0.3", - "typescript": "4.0.5", - "webpack-sources": "2.0.1" - }, - "dependencies": { - "tslib": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", - "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", - "dev": true - } - } - }, - "@angular-devkit/build-webpack": { - "version": "0.1100.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1100.6.tgz", - "integrity": "sha512-kK0FlpYJHP25o1yzIGHQqIvO5kp+p6V5OwGpD2GGRZLlJqd3WdjY5DxnyZoX3/IofO6KsTnmm76fzTRqc62z/Q==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.1100.6", - "@angular-devkit/core": "11.0.6", - "rxjs": "6.6.3" - } - }, - "@angular-devkit/core": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-11.0.6.tgz", - "integrity": "sha512-nhvU5hH01r9qcexAqvIFU233treWWeW3ncs9UFYjD9Hys9sDSvqC3+bvGvl9vCG5FsyY7oDsjaVAipyUc+SFAg==", - "dev": true, - "requires": { - "ajv": "6.12.6", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.3", - "source-map": "0.7.3" - } - }, - "@angular-devkit/schematics": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-11.0.6.tgz", - "integrity": "sha512-hCyu/SSSiC6dKl/NxdWctknIrBqKR6pRe7DMArWowrZX6P9oi36LpKEFnKutE8+tXjsOqQj8XMBq9L64sXZWqg==", - "dev": true, - "requires": { - "@angular-devkit/core": "11.0.6", - "ora": "5.1.0", - "rxjs": "6.6.3" - } - }, - "@angular/animations": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-11.0.7.tgz", - "integrity": "sha512-P3cluDGIsaj7vqvqIGW7xFCIXWa1lJDsHsmY3Fexk+ZVCncokftp5ZUANb2+DwOD3BPgd/WjBdXVjwzFQFsoVA==", - "requires": { - "tslib": "^2.0.0" - } - }, - "@angular/cdk": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-11.0.3.tgz", - "integrity": "sha512-hgbJXvZURKBnZawwxUrsZE/3a+HCJh2UhoLIng3cn5Q+WIW/4a37knDl8B9DYKBWrCqeINXNcUHVSKkWc/gjCA==", - "requires": { - "parse5": "^5.0.0", - "tslib": "^2.0.0" - }, - "dependencies": { - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "optional": true - } - } - }, - "@angular/cli": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.0.6.tgz", - "integrity": "sha512-bwrXXyU23HjUlFl0CNCU+XMGa/enooqpMLcTAA15StVpKFHyaA4c57il/aqu+1IuB+zR6rGDzhAABuvRcHd+mQ==", - "dev": true, - "requires": { - "@angular-devkit/architect": "0.1100.6", - "@angular-devkit/core": "11.0.6", - "@angular-devkit/schematics": "11.0.6", - "@schematics/angular": "11.0.6", - "@schematics/update": "0.1100.6", - "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.1", - "debug": "4.2.0", - "ini": "1.3.6", - "inquirer": "7.3.3", - "npm-package-arg": "8.1.0", - "npm-pick-manifest": "6.1.0", - "open": "7.3.0", - "pacote": "9.5.12", - "resolve": "1.18.1", - "rimraf": "3.0.2", - "semver": "7.3.2", - "symbol-observable": "2.0.3", - "universal-analytics": "0.4.23", - "uuid": "8.3.1" - }, - "dependencies": { - "debug": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", - "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "resolve": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", - "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", - "dev": true, - "requires": { - "is-core-module": "^2.0.0", - "path-parse": "^1.0.6" - } - }, - "uuid": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", - "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==", - "dev": true - } - } - }, - "@angular/common": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-11.0.7.tgz", - "integrity": "sha512-9VuT9qrSP7Q91Wp276DDieCIZiTBrpLNoJzK/RygQShTymCVPg4Dsl3tQUKaHBPx9MexeqRG/HjN02DVpeqtsA==", - "requires": { - "tslib": "^2.0.0" - } - }, - "@angular/compiler": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-11.0.7.tgz", - "integrity": "sha512-U+aGn6lP3iB184D2Z+OSK5vCwNtxtsF59T7nNJBMCCwcN7Qc5tfDBbSj1GI11XrIiuuoOxXbAp9BKS0dAyNCWw==", - "requires": { - "tslib": "^2.0.0" - } - }, - "@angular/compiler-cli": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-11.0.7.tgz", - "integrity": "sha512-04agqIPmw1exMeZjJmRzeYFgTEXYJ7gKD8TZipsGlu99uU/NQJIsetpzR7/bhPAKDH6YO0p/uavxV0nRpSTIQw==", - "dev": true, - "requires": { - "@babel/core": "^7.8.6", - "@babel/types": "^7.8.6", - "canonical-path": "1.0.0", - "chokidar": "^3.0.0", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.7.2", - "fs-extra": "4.0.2", - "magic-string": "^0.25.0", - "minimist": "^1.2.0", - "reflect-metadata": "^0.1.2", - "semver": "^6.3.0", - "source-map": "^0.6.1", - "sourcemap-codec": "^1.4.8", - "tslib": "^2.0.0", - "yargs": "^16.1.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "y18n": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", - "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true - } - } - }, - "@angular/core": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-11.0.7.tgz", - "integrity": "sha512-Kj5uRZoK5+xfMTjkP3tw8oIF5hKTnoF9Bwh5m9GUKqg1wHVKOJcT5JBIEMc8qPyiFgALREA01reIzQdGMjX36A==", - "requires": { - "tslib": "^2.0.0" - } - }, - "@angular/forms": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-11.0.7.tgz", - "integrity": "sha512-+3A+SciMyHTdUwkKUz4XzC1DSYexQEbFLe0PKQIFSFOROmbssjnWJv7yO2HbzCpGa7oGKPYNlE5twYWyLxpvFg==", - "requires": { - "tslib": "^2.0.0" - } - }, - "@angular/material": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-11.0.3.tgz", - "integrity": "sha512-YTHmtKGwjAEFAOOmuivcwnINMPHxvM7iZQpTgZqDKNiqiv53cwry2Ctb54suRNT+D794z59D0/r+YocGkzFv3w==", - "requires": { - "tslib": "^2.0.0" - } - }, - "@angular/platform-browser": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-11.0.7.tgz", - "integrity": "sha512-W8Wt8jMUjcbpqGtqrNWAj0p7CLdjOxgVlbrgBXTbaoqdchvXH85YzGr7ohA3MuE61H90OcVK9OhfYQk5o6joSg==", - "requires": { - "tslib": "^2.0.0" - } - }, - "@angular/platform-browser-dynamic": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.0.7.tgz", - "integrity": "sha512-pUXCum1Z2DZV/34WR4Vfmkc5nWxbmVdwAA9pXbAarwAYqHIqOzX8rpRaHsuHBAR+SK+VH+xjproeLgsVfV8FSA==", - "requires": { - "tslib": "^2.0.0" - } - }, - "@angular/router": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-11.0.7.tgz", - "integrity": "sha512-oh/MOPRSOCLRPsM/3CVUNYZ3pz3g+CzLOk5Vad/zFJmnGwjA/lQGJo2pl7VXVq3RF7MieaHlDWG5TexGlXAP5w==", - "requires": { - "tslib": "^2.0.0" - } - }, - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/compat-data": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.7.tgz", - "integrity": "sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw==", - "dev": true - }, - "@babel/core": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz", - "integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.1", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.1", - "@babel/parser": "^7.12.3", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz", - "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz", - "integrity": "sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.10" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz", - "integrity": "sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.12.5", - "@babel/helper-validator-option": "^7.12.1", - "browserslist": "^4.14.5", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz", - "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.12.1", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.7.tgz", - "integrity": "sha512-idnutvQPdpbduutvi3JVfEgcVIHooQnhvhx0Nk9isOINOIGYkZea1Pk2JlJRiUnMefrlvr0vkByATBY/mB4vjQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "regexpu-core": "^4.7.1" - } - }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz", - "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" - }, - "dependencies": { - "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" - } - } - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "dev": true, - "requires": { - "@babel/types": "^7.12.10" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", - "dev": true, - "requires": { - "@babel/types": "^7.12.7" - } - }, - "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.5" - } - }, - "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", - "dev": true, - "requires": { - "@babel/types": "^7.12.10" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz", - "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" - } - }, - "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz", - "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.1" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "dev": true, - "requires": { - "@babel/types": "^7.12.11" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz", - "integrity": "sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.12.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz", - "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.12.tgz", - "integrity": "sha512-nrz9y0a4xmUrRq51bYkWJIO5SBZyG2ys2qinHsN0zHDHVsUaModrkpyWWWXfGqYQmOL3x9sQIcTNN/pBGpo09A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1", - "@babel/plugin-syntax-async-generators": "^7.8.0" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz", - "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz", - "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz", - "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz", - "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz", - "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz", - "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz", - "integrity": "sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz", - "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.12.1" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz", - "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz", - "integrity": "sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz", - "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz", - "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz", - "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz", - "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz", - "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz", - "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.12.1" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz", - "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz", - "integrity": "sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz", - "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.10.4", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz", - "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz", - "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz", - "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz", - "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz", - "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz", - "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz", - "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz", - "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz", - "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz", - "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz", - "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.12.1", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz", - "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-identifier": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz", - "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz", - "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz", - "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz", - "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.12.1" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz", - "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz", - "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz", - "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz", - "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz", - "integrity": "sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "resolve": "^1.8.1", - "semver": "^5.5.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz", - "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz", - "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz", - "integrity": "sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz", - "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz", - "integrity": "sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz", - "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz", - "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/preset-env": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.12.1.tgz", - "integrity": "sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.12.1", - "@babel/helper-compilation-targets": "^7.12.1", - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-validator-option": "^7.12.1", - "@babel/plugin-proposal-async-generator-functions": "^7.12.1", - "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-proposal-dynamic-import": "^7.12.1", - "@babel/plugin-proposal-export-namespace-from": "^7.12.1", - "@babel/plugin-proposal-json-strings": "^7.12.1", - "@babel/plugin-proposal-logical-assignment-operators": "^7.12.1", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", - "@babel/plugin-proposal-numeric-separator": "^7.12.1", - "@babel/plugin-proposal-object-rest-spread": "^7.12.1", - "@babel/plugin-proposal-optional-catch-binding": "^7.12.1", - "@babel/plugin-proposal-optional-chaining": "^7.12.1", - "@babel/plugin-proposal-private-methods": "^7.12.1", - "@babel/plugin-proposal-unicode-property-regex": "^7.12.1", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.12.1", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.12.1", - "@babel/plugin-transform-arrow-functions": "^7.12.1", - "@babel/plugin-transform-async-to-generator": "^7.12.1", - "@babel/plugin-transform-block-scoped-functions": "^7.12.1", - "@babel/plugin-transform-block-scoping": "^7.12.1", - "@babel/plugin-transform-classes": "^7.12.1", - "@babel/plugin-transform-computed-properties": "^7.12.1", - "@babel/plugin-transform-destructuring": "^7.12.1", - "@babel/plugin-transform-dotall-regex": "^7.12.1", - "@babel/plugin-transform-duplicate-keys": "^7.12.1", - "@babel/plugin-transform-exponentiation-operator": "^7.12.1", - "@babel/plugin-transform-for-of": "^7.12.1", - "@babel/plugin-transform-function-name": "^7.12.1", - "@babel/plugin-transform-literals": "^7.12.1", - "@babel/plugin-transform-member-expression-literals": "^7.12.1", - "@babel/plugin-transform-modules-amd": "^7.12.1", - "@babel/plugin-transform-modules-commonjs": "^7.12.1", - "@babel/plugin-transform-modules-systemjs": "^7.12.1", - "@babel/plugin-transform-modules-umd": "^7.12.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.12.1", - "@babel/plugin-transform-new-target": "^7.12.1", - "@babel/plugin-transform-object-super": "^7.12.1", - "@babel/plugin-transform-parameters": "^7.12.1", - "@babel/plugin-transform-property-literals": "^7.12.1", - "@babel/plugin-transform-regenerator": "^7.12.1", - "@babel/plugin-transform-reserved-words": "^7.12.1", - "@babel/plugin-transform-shorthand-properties": "^7.12.1", - "@babel/plugin-transform-spread": "^7.12.1", - "@babel/plugin-transform-sticky-regex": "^7.12.1", - "@babel/plugin-transform-template-literals": "^7.12.1", - "@babel/plugin-transform-typeof-symbol": "^7.12.1", - "@babel/plugin-transform-unicode-escapes": "^7.12.1", - "@babel/plugin-transform-unicode-regex": "^7.12.1", - "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.12.1", - "core-js-compat": "^3.6.2", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", - "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/runtime": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz", - "integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.10.tgz", - "integrity": "sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.10", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.12.10", - "@babel/types": "^7.12.10", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - }, - "dependencies": { - "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "dev": true, - "requires": { - "@babel/types": "^7.12.11", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.11.tgz", - "integrity": "sha512-ukA9SQtKThINm++CX1CwmliMrE54J6nIYB5XTwL5f/CLFW9owfls+YSU8tVW15RQ2w+a3fSbPjC6HdQNtWZkiA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true - }, - "@jsdevtools/coverage-istanbul-loader": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz", - "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==", - "dev": true, - "requires": { - "convert-source-map": "^1.7.0", - "istanbul-lib-instrument": "^4.0.3", - "loader-utils": "^2.0.0", - "merge-source-map": "^1.1.0", - "schema-utils": "^2.7.0" - } - }, - "@ngtools/webpack": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.0.6.tgz", - "integrity": "sha512-vf5YNEpXWRa0fKC/BRq5sVVj2WnEqW8jn14YQRHwVt5ppUeyu8IKUF69p6W1MwZMgMqMaw/vPQ8LI5cFbyf3uw==", - "dev": true, - "requires": { - "@angular-devkit/core": "11.0.6", - "enhanced-resolve": "5.3.1", - "webpack-sources": "2.0.1" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.4", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.4", - "fastq": "^1.6.0" - } - }, - "@npmcli/move-file": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz", - "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==", - "dev": true, - "requires": { - "mkdirp": "^1.0.4" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "@schematics/angular": { - "version": "11.0.6", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-11.0.6.tgz", - "integrity": "sha512-XUcpOrlcp55PBHrgpIVx69lnhDY6ro35BSRmqNmjXik56qcOkfvdki8vvyW9EsWvu9/sfBSsVDdparlbVois7w==", - "dev": true, - "requires": { - "@angular-devkit/core": "11.0.6", - "@angular-devkit/schematics": "11.0.6", - "jsonc-parser": "2.3.1" - } - }, - "@schematics/update": { - "version": "0.1100.6", - "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1100.6.tgz", - "integrity": "sha512-+B8n+k+zZ3VYOhjNBsLqzjp8O9ZdUWgdpf9L8XAA7mh/oPwufXpExyEc66uAS07imvUMmjz6i8E2eNWV/IjBJg==", - "dev": true, - "requires": { - "@angular-devkit/core": "11.0.6", - "@angular-devkit/schematics": "11.0.6", - "@yarnpkg/lockfile": "1.1.0", - "ini": "1.3.6", - "npm-package-arg": "^8.0.0", - "pacote": "9.5.12", - "semver": "7.3.2", - "semver-intersect": "1.4.0" - } - }, - "@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", - "dev": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/jasmine": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.2.tgz", - "integrity": "sha512-AzfesNFLvOs6Q1mHzIsVJXSeUnqVh4ZHG8ngygKJfbkcSLwzrBVm/LKa+mR8KrOfnWtUL47112gde1MC0IXqpQ==", - "dev": true - }, - "@types/json-schema": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", - "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", - "dev": true - }, - "@types/jwt-decode": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/jwt-decode/-/jwt-decode-3.1.0.tgz", - "integrity": "sha512-tthwik7TKkou3mVnBnvVuHnHElbjtdbM63pdBCbZTirCt3WAdM73Y79mOri7+ljsS99ZVwUFZHLMxJuJnv/z1w==", - "requires": { - "jwt-decode": "*" - } - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/node": { - "version": "12.19.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.12.tgz", - "integrity": "sha512-UwfL2uIU9arX/+/PRcIkT08/iBadGN2z6ExOROA2Dh5mAuWTBj6iJbQX4nekiV5H8cTrEG569LeX+HRco9Cbxw==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/q": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz", - "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==", - "dev": true - }, - "@types/selenium-webdriver": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", - "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==", - "dev": true - }, - "@types/source-list-map": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", - "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", - "dev": true - }, - "@types/webpack-sources": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.8.tgz", - "integrity": "sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/source-list-map": "*", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", - "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", - "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", - "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", - "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", - "dev": true - }, - "@webassemblyjs/helper-code-frame": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", - "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", - "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "@webassemblyjs/helper-fsm": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", - "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", - "dev": true - }, - "@webassemblyjs/helper-module-context": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", - "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", - "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", - "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", - "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", - "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/helper-wasm-section": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-opt": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", - "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", - "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", - "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "@webassemblyjs/wast-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", - "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/floating-point-hex-parser": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-code-frame": "1.9.0", - "@webassemblyjs/helper-fsm": "1.9.0", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abab": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", - "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==", - "dev": true - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dev": true, - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true - }, - "adjust-sourcemap-loader": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz", - "integrity": "sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - } - }, - "adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", - "dev": true - }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "agentkeepalive": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", - "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", - "dev": true, - "requires": { - "humanize-ms": "^1.2.1" - } - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - } - }, - "ansi-html": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", - "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", - "dev": true - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "app-root-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz", - "integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==", - "dev": true - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "aria-query": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", - "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", - "dev": true, - "requires": { - "ast-types-flow": "0.0.7", - "commander": "^2.11.0" - } - }, - "arity-n": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz", - "integrity": "sha1-2edrEXM+CFacCEeuezmyhgswt0U=", - "dev": true - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "arraybuffer.slice": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - } - } - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", - "dev": true - }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "autoprefixer": { - "version": "9.8.6", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz", - "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "caniuse-lite": "^1.0.30001109", - "colorette": "^1.2.1", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.32", - "postcss-value-parser": "^4.1.0" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true - }, - "axobject-query": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", - "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==", - "dev": true, - "requires": { - "ast-types-flow": "0.0.7" - } - }, - "babel-loader": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", - "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", - "dev": true, - "requires": { - "find-cache-dir": "^2.1.0", - "loader-utils": "^1.4.0", - "mkdirp": "^0.5.3", - "pify": "^4.0.1", - "schema-utils": "^2.6.5" - }, - "dependencies": { - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base64-arraybuffer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz", - "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=", - "dev": true - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true - }, - "batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true - }, - "blob": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", - "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", - "dev": true - }, - "blocking-proxy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", - "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "bn.js": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", - "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==", - "dev": true - }, - "body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dev": true, - "requires": { - "bytes": "3.1.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.7.0", - "raw-body": "2.4.0", - "type-is": "~1.6.17" - }, - "dependencies": { - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "bonjour": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", - "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", - "dev": true, - "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" - } - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "dev": true, - "requires": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", - "dev": true, - "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "browserslist": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.1.tgz", - "integrity": "sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001173", - "colorette": "^1.2.1", - "electron-to-chromium": "^1.3.634", - "escalade": "^3.1.1", - "node-releases": "^1.1.69" - } - }, - "browserstack": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz", - "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - } - }, - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-indexof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", - "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "builtins": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", - "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", - "dev": true - }, - "cacache": { - "version": "15.0.5", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz", - "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==", - "dev": true, - "requires": { - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.0", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "call-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", - "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.0" - } - }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - }, - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30001173", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001173.tgz", - "integrity": "sha512-R3aqmjrICdGCTAnSXtNyvWYMK3YtV5jwudbq0T7nN9k4kmE4CBuwPqyJ+KBzepSTh0huivV2gLbSMEzTTmfeYw==", - "dev": true - }, - "canonical-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", - "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chokidar": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "circular-dependency-plugin": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz", - "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.5.0.tgz", - "integrity": "sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==", - "dev": true - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dev": true, - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - } - }, - "codelyzer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.1.tgz", - "integrity": "sha512-cOyGQgMdhnRYtW2xrJUNrNYDjEgwQ+BrE2y93Bwz3h4DJ6vJRLfupemU5N3pbYsUlBHJf0u1j1UGk+NLW4d97g==", - "dev": true, - "requires": { - "@angular/compiler": "9.0.0", - "@angular/core": "9.0.0", - "app-root-path": "^3.0.0", - "aria-query": "^3.0.0", - "axobject-query": "2.0.2", - "css-selector-tokenizer": "^0.7.1", - "cssauron": "^1.4.0", - "damerau-levenshtein": "^1.0.4", - "rxjs": "^6.5.3", - "semver-dsl": "^1.0.1", - "source-map": "^0.5.7", - "sprintf-js": "^1.1.2", - "tslib": "^1.10.0", - "zone.js": "~0.10.3" - }, - "dependencies": { - "@angular/compiler": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz", - "integrity": "sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==", - "dev": true - }, - "@angular/core": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz", - "integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", - "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", - "dev": true, - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.4" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "color-string": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", - "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", - "dev": true, - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colorette": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", - "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, - "compose-function": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz", - "integrity": "sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=", - "dev": true, - "requires": { - "arity-n": "^1.0.4" - } - }, - "compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "requires": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "connect-history-api-fallback": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", - "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", - "dev": true - }, - "console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.2.1.tgz", - "integrity": "sha512-VH2ZTMIBsx4p++Lmpg77adZ0KUyM5gFR/9cuTrbneNnJlcQXUFvsNariPqq2dq2kV3F2skHiDGPQCyKWy1+U0Q==", - "dev": true, - "requires": { - "cacache": "^15.0.5", - "fast-glob": "^3.2.4", - "find-cache-dir": "^3.3.1", - "glob-parent": "^5.1.1", - "globby": "^11.0.1", - "loader-utils": "^2.0.0", - "normalize-path": "^3.0.0", - "p-limit": "^3.0.2", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "webpack-sources": "^1.4.3" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - } - } - }, - "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", - "dev": true - }, - "core-js-compat": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.2.tgz", - "integrity": "sha512-LO8uL9lOIyRRrQmZxHZFl1RV+ZbcsAkFWTktn5SmH40WgLtSNYN4m4W2v9ONT147PxBY/XrRhrWq8TlvObyUjQ==", - "dev": true, - "requires": { - "browserslist": "^4.16.0", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dev": true, - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - } - }, - "create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "css": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", - "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "source-map": "^0.6.1", - "source-map-resolve": "^0.5.2", - "urix": "^0.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, - "css-declaration-sorter": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", - "dev": true, - "requires": { - "postcss": "^7.0.1", - "timsort": "^0.3.0" - } - }, - "css-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-4.3.0.tgz", - "integrity": "sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg==", - "dev": true, - "requires": { - "camelcase": "^6.0.0", - "cssesc": "^3.0.0", - "icss-utils": "^4.1.1", - "loader-utils": "^2.0.0", - "postcss": "^7.0.32", - "postcss-modules-extract-imports": "^2.0.0", - "postcss-modules-local-by-default": "^3.0.3", - "postcss-modules-scope": "^2.2.0", - "postcss-modules-values": "^3.0.0", - "postcss-value-parser": "^4.1.0", - "schema-utils": "^2.7.1", - "semver": "^7.3.2" - } - }, - "css-parse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz", - "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=", - "dev": true, - "requires": { - "css": "^2.0.0" - } - }, - "css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", - "dev": true - }, - "css-selector-tokenizer": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", - "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, - "css-tree": { - "version": "1.0.0-alpha.37", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", - "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", - "dev": true, - "requires": { - "mdn-data": "2.0.4", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "css-what": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", - "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", - "dev": true - }, - "cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", - "dev": true, - "requires": { - "through": "X.X.X" - } - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true - }, - "cssnano": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", - "dev": true, - "requires": { - "cosmiconfig": "^5.0.0", - "cssnano-preset-default": "^4.0.7", - "is-resolvable": "^1.0.0", - "postcss": "^7.0.0" - } - }, - "cssnano-preset-default": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", - "dev": true, - "requires": { - "css-declaration-sorter": "^4.0.1", - "cssnano-util-raw-cache": "^4.0.1", - "postcss": "^7.0.0", - "postcss-calc": "^7.0.1", - "postcss-colormin": "^4.0.3", - "postcss-convert-values": "^4.0.1", - "postcss-discard-comments": "^4.0.2", - "postcss-discard-duplicates": "^4.0.2", - "postcss-discard-empty": "^4.0.1", - "postcss-discard-overridden": "^4.0.1", - "postcss-merge-longhand": "^4.0.11", - "postcss-merge-rules": "^4.0.3", - "postcss-minify-font-values": "^4.0.2", - "postcss-minify-gradients": "^4.0.2", - "postcss-minify-params": "^4.0.2", - "postcss-minify-selectors": "^4.0.2", - "postcss-normalize-charset": "^4.0.1", - "postcss-normalize-display-values": "^4.0.2", - "postcss-normalize-positions": "^4.0.2", - "postcss-normalize-repeat-style": "^4.0.2", - "postcss-normalize-string": "^4.0.2", - "postcss-normalize-timing-functions": "^4.0.2", - "postcss-normalize-unicode": "^4.0.1", - "postcss-normalize-url": "^4.0.1", - "postcss-normalize-whitespace": "^4.0.2", - "postcss-ordered-values": "^4.1.2", - "postcss-reduce-initial": "^4.0.3", - "postcss-reduce-transforms": "^4.0.2", - "postcss-svgo": "^4.0.2", - "postcss-unique-selectors": "^4.0.1" - } - }, - "cssnano-util-get-arguments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", - "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", - "dev": true - }, - "cssnano-util-get-match": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", - "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", - "dev": true - }, - "cssnano-util-raw-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "cssnano-util-same-parent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", - "dev": true - }, - "csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", - "dev": true, - "requires": { - "css-tree": "^1.1.2" - }, - "dependencies": { - "css-tree": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.2.tgz", - "integrity": "sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==", - "dev": true, - "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" - } - }, - "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", - "dev": true - }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", - "dev": true - }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "damerau-levenshtein": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", - "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "date-format": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz", - "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==", - "dev": true - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-equal": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", - "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", - "dev": true, - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - } - }, - "default-gateway": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", - "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "ip-regex": "^2.1.0" - } - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, - "dependencies": { - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true - }, - "dependency-graph": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", - "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", - "dev": true - }, - "des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true - }, - "detect-node": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", - "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", - "dev": true - }, - "di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", - "dev": true - }, - "dns-packet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", - "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", - "dev": true, - "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" - } - }, - "dns-txt": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", - "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", - "dev": true, - "requires": { - "buffer-indexof": "^1.0.0" - } - }, - "dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", - "dev": true, - "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "dev": true, - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", - "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", - "dev": true - } - } - }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", - "dev": true - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.634", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.634.tgz", - "integrity": "sha512-QPrWNYeE/A0xRvl/QP3E0nkaEvYUvH3gM04ZWYtIa6QlSpEetRlRI1xvQ7hiMIySHHEV+mwDSX8Kj4YZY6ZQAw==", - "dev": true - }, - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true - }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "requires": { - "iconv-lite": "^0.6.2" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "engine.io": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", - "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "0.3.1", - "debug": "~4.1.0", - "engine.io-parser": "~2.2.0", - "ws": "^7.1.2" - }, - "dependencies": { - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ws": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz", - "integrity": "sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==", - "dev": true - } - } - }, - "engine.io-client": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz", - "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==", - "dev": true, - "requires": { - "component-emitter": "~1.3.0", - "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.2.0", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parseqs": "0.0.6", - "parseuri": "0.0.6", - "ws": "~6.1.0", - "xmlhttprequest-ssl": "~1.5.4", - "yeast": "0.1.2" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "parseqs": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", - "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==", - "dev": true - }, - "parseuri": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", - "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==", - "dev": true - }, - "ws": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", - "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "engine.io-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz", - "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", - "base64-arraybuffer": "0.1.4", - "blob": "0.0.5", - "has-binary2": "~1.0.2" - } - }, - "enhanced-resolve": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.3.1.tgz", - "integrity": "sha512-G1XD3MRGrGfNcf6Hg0LVZG7GIKcYkbfHa5QMxt1HDUTdYoXH0JR1xXyg+MaKLF73E9A27uWNVxvFivNRYeUB6w==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.0.0" - } - }, - "ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", - "dev": true - }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true - }, - "err-code": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", - "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", - "dev": true - }, - "errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", - "dev": true, - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dev": true, - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", - "dev": true - }, - "eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", - "dev": true, - "requires": { - "original": "^1.0.0" - } - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "express": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", - "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dev": true, - "requires": { - "accepts": "~1.3.7", - "array-flatten": "1.1.1", - "body-parser": "1.19.0", - "content-disposition": "0.5.3", - "content-type": "~1.0.4", - "cookie": "0.4.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.5", - "qs": "6.7.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.1.2", - "send": "0.17.1", - "serve-static": "1.14.1", - "setprototypeof": "1.1.1", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "ext": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", - "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", - "dev": true, - "requires": { - "type": "^2.0.0" - }, - "dependencies": { - "type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", - "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==", - "dev": true - } - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", - "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, - "fastq": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.10.0.tgz", - "integrity": "sha512-NL2Qc5L3iQEsyYzweq7qfgy5OtXCmGzGvhElGEd/SoFWEMOEczNh5s5ocaF01HDetxz+p8ecjNPA6cZxxIHmzA==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - }, - "figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", - "dev": true - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-loader": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.1.1.tgz", - "integrity": "sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "find-cache-dir": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", - "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "follow-redirects": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz", - "integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-extra": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz", - "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "genfun": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", - "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", - "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.2.tgz", - "integrity": "sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "guid-typescript": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", - "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==" - }, - "handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - } - } - }, - "has-binary2": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", - "dev": true, - "requires": { - "isarray": "2.0.1" - }, - "dependencies": { - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - } - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hosted-git-info": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.7.tgz", - "integrity": "sha512-fWqc0IcuXs+BmE9orLDyVykAG9GJtGLGuZAAqgcckPgv5xad4AcXGIv8galtQvlwutxSlaMcdw7BUtq2EIvqCQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", - "dev": true - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", - "dev": true - }, - "html-comment-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", - "dev": true - }, - "html-entities": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz", - "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", - "dev": true - }, - "http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", - "dev": true - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } - } - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "http-proxy-middleware": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", - "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", - "dev": true, - "requires": { - "http-proxy": "^1.17.0", - "is-glob": "^4.0.0", - "lodash": "^4.17.11", - "micromatch": "^3.1.10" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", - "dev": true, - "requires": { - "ms": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-utils": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", - "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", - "dev": true, - "requires": { - "postcss": "^7.0.14" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "ignore-walk": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", - "dev": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "dev": true, - "optional": true - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", - "dev": true - }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - } - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.6.tgz", - "integrity": "sha512-IZUoxEjNjubzrmvzZU4lKP7OnYmX72XRl3sqkfJhBKweKi5rnGi5+IUdlj/H1M+Ip5JQ1WzaDMOBRY90Ajc5jg==", - "dev": true - }, - "inquirer": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", - "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.19", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "internal-ip": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", - "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", - "dev": true, - "requires": { - "default-gateway": "^4.2.0", - "ipaddr.js": "^1.9.0" - } - }, - "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arguments": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", - "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", - "dev": true, - "requires": { - "call-bind": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true - }, - "is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dev": true, - "requires": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - } - }, - "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", - "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", - "dev": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "requires": { - "is-path-inside": "^2.1.0" - } - }, - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", - "dev": true, - "requires": { - "html-comment-regex": "^1.1.0" - } - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isbinaryfile": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.6.tgz", - "integrity": "sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", - "dev": true, - "requires": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" - }, - "dependencies": { - "jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", - "dev": true - } - } - }, - "jasmine-core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz", - "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==", - "dev": true - }, - "jasmine-spec-reporter": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz", - "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==", - "dev": true, - "requires": { - "colors": "1.4.0" - } - }, - "jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", - "dev": true - }, - "jest-worker": { - "version": "26.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.5.0.tgz", - "integrity": "sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json3": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", - "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", - "dev": true - }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", - "dev": true - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jszip": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz", - "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==", - "dev": true, - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "set-immediate-shim": "~1.0.1" - } - }, - "jwt-decode": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", - "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" - }, - "karma": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/karma/-/karma-5.1.1.tgz", - "integrity": "sha512-xAlOr5PMqUbiKXSv5PCniHWV3aiwj6wIZ0gUVcwpTCPVQm/qH2WAMFWxtnpM6KJqhkRWrIpovR4Rb0rn8GtJzQ==", - "dev": true, - "requires": { - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.0.0", - "colors": "^1.4.0", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "flatted": "^2.0.2", - "glob": "^7.1.6", - "graceful-fs": "^4.2.4", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.6", - "lodash": "^4.17.15", - "log4js": "^6.2.1", - "mime": "^2.4.5", - "minimatch": "^3.0.4", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^2.3.0", - "source-map": "^0.6.1", - "tmp": "0.2.1", - "ua-parser-js": "0.7.21", - "yargs": "^15.3.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "mime": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz", - "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==", - "dev": true - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "karma-chrome-launcher": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz", - "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==", - "dev": true, - "requires": { - "which": "^1.2.1" - } - }, - "karma-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.0.3.tgz", - "integrity": "sha512-atDvLQqvPcLxhED0cmXYdsPMCQuh6Asa9FMZW1bhNqlVEhJoB9qyZ2BY1gu7D/rr5GLGb5QzYO4siQskxaWP/g==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.1", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.0", - "minimatch": "^3.0.4" - } - }, - "karma-jasmine": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz", - "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==", - "dev": true, - "requires": { - "jasmine-core": "^3.6.0" - } - }, - "karma-jasmine-html-reporter": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz", - "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==", - "dev": true - }, - "karma-source-map-support": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", - "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", - "dev": true, - "requires": { - "source-map-support": "^0.5.5" - } - }, - "killable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", - "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "klona": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz", - "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==", - "dev": true - }, - "less": { - "version": "3.12.2", - "resolved": "https://registry.npmjs.org/less/-/less-3.12.2.tgz", - "integrity": "sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q==", - "dev": true, - "requires": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "native-request": "^1.0.5", - "source-map": "~0.6.0", - "tslib": "^1.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "less-loader": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-7.0.2.tgz", - "integrity": "sha512-7MKlgjnkCf63E3Lv6w2FvAEgLMx3d/tNBExITcanAq7ys5U8VPWT3F6xcRjYmdNfkoQ9udoVFb1r2azSiTnD6w==", - "dev": true, - "requires": { - "klona": "^2.0.4", - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "license-webpack-plugin": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.1.tgz", - "integrity": "sha512-yhqTmlYIEpZWA122lf6E0G8+rkn0AzoQ1OpzUKKs/lXUqG1plmGnwmkuuPlfggzJR5y6DLOdot/Tv00CC51CeQ==", - "dev": true, - "requires": { - "@types/webpack-sources": "^0.1.5", - "webpack-sources": "^1.2.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - } - } - }, - "lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "requires": { - "immediate": "~3.0.5" - } - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", - "dev": true, - "requires": { - "chalk": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "log4js": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz", - "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==", - "dev": true, - "requires": { - "date-format": "^3.0.0", - "debug": "^4.1.1", - "flatted": "^2.0.1", - "rfdc": "^1.1.4", - "streamroller": "^2.2.4" - } - }, - "loglevel": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz", - "integrity": "sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "make-fetch-happen": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz", - "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", - "dev": true, - "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^12.0.0", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" - }, - "dependencies": { - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "mdn-data": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", - "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true - }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "dev": true, - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "mini-css-extract-plugin": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.2.1.tgz", - "integrity": "sha512-G3yw7/TQaPfkuiR73MDcyiqhyP8SnbmLhUbpC76H+wtQxA6wfKhMCQOCb6wnPK0dQbjORAeOILQqEesg4/wF7A==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "webpack-sources": "^1.1.0" - }, - "dependencies": { - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - } - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "minipass": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", - "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - } - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - } - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "multicast-dns": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", - "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", - "dev": true, - "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" - } - }, - "multicast-dns-service-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", - "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "native-request": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.0.8.tgz", - "integrity": "sha512-vU2JojJVelUGp6jRcLwToPoWGxSx23z/0iX+I77J3Ht17rf2INGjrhOoQnjVo60nQd8wVsgzKkPfRXBiVdD2ag==", - "dev": true, - "optional": true - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-fetch-npm": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz", - "integrity": "sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg==", - "dev": true, - "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" - } - }, - "node-forge": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", - "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", - "dev": true - }, - "node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "node-releases": { - "version": "1.1.69", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.69.tgz", - "integrity": "sha512-DGIjo79VDEyAnRlfSqYTsy+yoHd2IOjJiKUozD2MV2D85Vso6Bug56mb9tT/fY5Urt0iqk01H7x+llAruDR2zA==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", - "dev": true - }, - "normalize.css": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", - "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" - }, - "npm-bundled": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", - "dev": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-install-checks": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", - "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", - "dev": true, - "requires": { - "semver": "^7.1.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true - }, - "npm-package-arg": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.0.tgz", - "integrity": "sha512-/ep6QDxBkm9HvOhOg0heitSd7JHA1U7y1qhhlRlteYYAi9Pdb/ZV7FW5aHpkrpM8+P+4p/jjR8zCyKPBMBjSig==", - "dev": true, - "requires": { - "hosted-git-info": "^3.0.6", - "semver": "^7.0.0", - "validate-npm-package-name": "^3.0.0" - } - }, - "npm-packlist": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", - "dev": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1", - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-pick-manifest": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz", - "integrity": "sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw==", - "dev": true, - "requires": { - "npm-install-checks": "^4.0.0", - "npm-package-arg": "^8.0.0", - "semver": "^7.0.0" - } - }, - "npm-registry-fetch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz", - "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==", - "dev": true, - "requires": { - "JSONStream": "^1.3.4", - "bluebird": "^3.5.1", - "figgy-pudding": "^3.4.1", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "npm-package-arg": "^6.1.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "npm-package-arg": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", - "dev": true, - "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - }, - "object-is": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.4.tgz", - "integrity": "sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz", - "integrity": "sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", - "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has": "^1.0.3" - } - }, - "obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz", - "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==", - "dev": true, - "requires": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - } - }, - "opn": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", - "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", - "dev": true, - "requires": { - "is-wsl": "^1.1.0" - }, - "dependencies": { - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - } - } - }, - "ora": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.1.0.tgz", - "integrity": "sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.4.0", - "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", - "mute-stream": "0.0.8", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "dev": true, - "requires": { - "url-parse": "^1.4.3" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-retry": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", - "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", - "dev": true, - "requires": { - "retry": "^0.12.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pacote": { - "version": "9.5.12", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.12.tgz", - "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.3", - "cacache": "^12.0.2", - "chownr": "^1.1.2", - "figgy-pudding": "^3.5.1", - "get-stream": "^4.1.0", - "glob": "^7.1.3", - "infer-owner": "^1.0.4", - "lru-cache": "^5.1.1", - "make-fetch-happen": "^5.0.0", - "minimatch": "^3.0.4", - "minipass": "^2.3.5", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-normalize-package-bin": "^1.0.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.12", - "npm-pick-manifest": "^3.0.0", - "npm-registry-fetch": "^4.0.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.1", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.6.0", - "ssri": "^6.0.1", - "tar": "^4.4.10", - "unique-filename": "^1.1.1", - "which": "^1.3.1" - }, - "dependencies": { - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dev": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dev": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "npm-package-arg": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz", - "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", - "dev": true, - "requires": { - "hosted-git-info": "^2.7.1", - "osenv": "^0.1.5", - "semver": "^5.6.0", - "validate-npm-package-name": "^3.0.0" - } - }, - "npm-pick-manifest": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz", - "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1", - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "dev": true, - "requires": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - }, - "dependencies": { - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - } - } - }, - "parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "dev": true, - "requires": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "parse5-html-rewriting-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", - "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", - "dev": true, - "requires": { - "parse5": "^6.0.1", - "parse5-sax-parser": "^6.0.1" - } - }, - "parse5-sax-parser": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", - "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", - "dev": true, - "requires": { - "parse5": "^6.0.1" - } - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "~1.0.0" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "pnp-webpack-plugin": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", - "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==", - "dev": true, - "requires": { - "ts-pnp": "^1.1.6" - } - }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "7.0.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", - "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-calc": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz", - "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", - "dev": true, - "requires": { - "postcss": "^7.0.27", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.0.2" - } - }, - "postcss-colormin": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "color": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-convert-values": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-discard-comments": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-duplicates": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-empty": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-discard-overridden": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-import": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", - "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", - "dev": true, - "requires": { - "postcss": "^7.0.1", - "postcss-value-parser": "^3.2.3", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-loader": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-4.0.4.tgz", - "integrity": "sha512-pntA9zIR14drQo84yGTjQJg1m7T0DkXR4vXYHBngiRZdJtEeCrojL6lOpqUanMzG375lIJbT4Yug85zC/AJWGw==", - "dev": true, - "requires": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.4", - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "semver": "^7.3.2" - }, - "dependencies": { - "cosmiconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", - "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "parse-json": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", - "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "postcss-merge-longhand": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", - "dev": true, - "requires": { - "css-color-names": "0.0.4", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "stylehacks": "^4.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-merge-rules": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "cssnano-util-same-parent": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0", - "vendors": "^1.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postcss-minify-font-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-minify-gradients": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "is-color-stop": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-minify-params": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "browserslist": "^4.0.0", - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "uniqs": "^2.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-minify-selectors": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postcss-modules-extract-imports": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", - "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", - "dev": true, - "requires": { - "postcss": "^7.0.5" - } - }, - "postcss-modules-local-by-default": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", - "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", - "dev": true, - "requires": { - "icss-utils": "^4.1.1", - "postcss": "^7.0.32", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - } - }, - "postcss-modules-scope": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", - "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", - "dev": true, - "requires": { - "postcss": "^7.0.6", - "postcss-selector-parser": "^6.0.0" - } - }, - "postcss-modules-values": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", - "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", - "dev": true, - "requires": { - "icss-utils": "^4.0.0", - "postcss": "^7.0.6" - } - }, - "postcss-normalize-charset": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - } - }, - "postcss-normalize-display-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-normalize-positions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-normalize-repeat-style": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-normalize-string": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", - "dev": true, - "requires": { - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-normalize-timing-functions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-normalize-unicode": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-normalize-url": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", - "dev": true, - "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-normalize-whitespace": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-ordered-values": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-reduce-initial": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0" - } - }, - "postcss-reduce-transforms": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-selector-parser": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz", - "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1", - "util-deprecate": "^1.0.2" - } - }, - "postcss-svgo": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", - "dev": true, - "requires": { - "is-svg": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" - }, - "dependencies": { - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - } - } - }, - "postcss-unique-selectors": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "postcss": "^7.0.0", - "uniqs": "^2.0.0" - } - }, - "postcss-value-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", - "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "promise-retry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", - "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", - "dev": true, - "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" - }, - "dependencies": { - "retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", - "dev": true - } - } - }, - "protoduck": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", - "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", - "dev": true, - "requires": { - "genfun": "^5.0.0" - } - }, - "protractor": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", - "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==", - "dev": true, - "requires": { - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", - "blocking-proxy": "^1.0.0", - "browserstack": "^1.5.1", - "chalk": "^1.1.3", - "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", - "q": "1.4.1", - "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", - "source-map-support": "~0.4.0", - "webdriver-js-extender": "2.1.0", - "webdriver-manager": "^12.1.7", - "yargs": "^15.3.1" - }, - "dependencies": { - "@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "webdriver-manager": { - "version": "12.1.7", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz", - "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==", - "dev": true, - "requires": { - "adm-zip": "^0.4.9", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "dev": true, - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true - } - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dev": true, - "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true - } - } - }, - "raw-loader": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz", - "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "dev": true, - "requires": { - "pify": "^2.3.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "dev": true - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" - } - }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, - "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regex-parser": { - "version": "2.2.11", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", - "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", - "dev": true - }, - "regexp.prototype.flags": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", - "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } - } - }, - "regexpu-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", - "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - } - }, - "regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", - "dev": true - }, - "regjsparser": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", - "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "reset-css": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/reset-css/-/reset-css-5.0.1.tgz", - "integrity": "sha512-VyuJdNFfp5x/W6e5wauJM59C02Vs0P22sxzZGhQMPaqu/NGTeFxlBFOOw3eq9vQd19gIDdZp7zi89ylyKOJ33Q==" - }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "resolve-url-loader": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz", - "integrity": "sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ==", - "dev": true, - "requires": { - "adjust-sourcemap-loader": "3.0.0", - "camelcase": "5.3.1", - "compose-function": "3.0.3", - "convert-source-map": "1.7.0", - "es6-iterator": "2.0.3", - "loader-utils": "1.2.3", - "postcss": "7.0.21", - "rework": "1.0.1", - "rework-visit": "1.0.0", - "source-map": "0.6.1" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "postcss": { - "version": "7.0.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz", - "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rework": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", - "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=", - "dev": true, - "requires": { - "convert-source-map": "^0.3.3", - "css": "^2.0.0" - }, - "dependencies": { - "convert-source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", - "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=", - "dev": true - } - } - }, - "rework-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", - "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=", - "dev": true - }, - "rfdc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", - "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", - "dev": true - }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", - "dev": true - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rollup": { - "version": "2.32.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.32.1.tgz", - "integrity": "sha512-Op2vWTpvK7t6/Qnm1TTh7VjEZZkN8RWgf0DHbkKzQBwNf748YhXbozHVefqpPp/Fuyk/PQPAnYsBxAEtlMvpUw==", - "dev": true, - "requires": { - "fsevents": "~2.1.2" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-parallel": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", - "integrity": "sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==", - "dev": true - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "rxjs": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", - "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "sass": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.27.0.tgz", - "integrity": "sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig==", - "dev": true, - "requires": { - "chokidar": ">=2.0.0 <4.0.0" - } - }, - "sass-loader": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.0.5.tgz", - "integrity": "sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w==", - "dev": true, - "requires": { - "klona": "^2.0.4", - "loader-utils": "^2.0.0", - "neo-async": "^2.6.2", - "schema-utils": "^3.0.0", - "semver": "^7.3.2" - }, - "dependencies": { - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "saucelabs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", - "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", - "dev": true, - "requires": { - "https-proxy-agent": "^2.2.1" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" - } - }, - "select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", - "dev": true - }, - "selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", - "dev": true, - "requires": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.1" - } - } - } - }, - "selfsigned": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", - "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", - "dev": true, - "requires": { - "node-forge": "^0.10.0" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - }, - "semver-dsl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", - "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", - "dev": true, - "requires": { - "semver": "^5.3.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "semver-intersect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", - "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", - "dev": true, - "requires": { - "semver": "^5.0.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", - "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.7.2", - "mime": "1.6.0", - "ms": "2.1.1", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", - "dev": true, - "requires": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "dev": true, - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - } - } - }, - "serve-static": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", - "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dev": true, - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dev": true, - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - } - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "socket.io": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", - "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", - "dev": true, - "requires": { - "debug": "~4.1.0", - "engine.io": "~3.4.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.3.0", - "socket.io-parser": "~3.4.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "socket.io-adapter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", - "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==", - "dev": true - }, - "socket.io-client": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", - "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", - "dev": true, - "requires": { - "backo2": "1.0.2", - "base64-arraybuffer": "0.1.5", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "~4.1.0", - "engine.io-client": "~3.4.0", - "has-binary2": "~1.0.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "socket.io-parser": "~3.3.0", - "to-array": "0.1.4" - }, - "dependencies": { - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - }, - "socket.io-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz", - "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==", - "dev": true, - "requires": { - "component-emitter": "~1.3.0", - "debug": "~3.1.0", - "isarray": "2.0.1" - }, - "dependencies": { - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - } - } - }, - "socket.io-parser": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", - "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "debug": "~4.1.0", - "isarray": "2.0.1" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "isarray": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", - "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", - "dev": true - } - } - }, - "sockjs": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", - "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", - "dev": true, - "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.4.0", - "websocket-driver": "0.6.5" - } - }, - "sockjs-client": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", - "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", - "dev": true, - "requires": { - "debug": "^3.2.5", - "eventsource": "^1.0.7", - "faye-websocket": "~0.11.1", - "inherits": "^2.0.3", - "json3": "^3.3.2", - "url-parse": "^1.4.3" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "faye-websocket": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", - "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", - "dev": true, - "requires": { - "websocket-driver": ">=0.5.1" - } - } - } - }, - "socks": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz", - "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", - "dev": true, - "requires": { - "ip": "1.1.5", - "smart-buffer": "^4.1.0" - } - }, - "socks-proxy-agent": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz", - "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", - "dev": true, - "requires": { - "agent-base": "~4.2.1", - "socks": "~2.3.2" - }, - "dependencies": { - "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - } - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - }, - "source-map-loader": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-1.1.2.tgz", - "integrity": "sha512-bjf6eSENOYBX4JZDfl9vVLNsGAQ6Uz90fLmOazcmMcyDYOBFsGxPNn83jXezWLY9bJsVAo1ObztxPcV8HAbjVA==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "iconv-lite": "^0.6.2", - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0", - "source-map": "^0.6.1", - "whatwg-mimetype": "^2.3.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", - "dev": true - }, - "spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - } - }, - "spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "speed-measure-webpack-plugin": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.3.tgz", - "integrity": "sha512-2ljD4Ch/rz2zG3HsLsnPfp23osuPBS0qPuz9sGpkNXTN1Ic4M+W9xB8l8rS8ob2cO4b1L+WTJw/0AJwWYVgcxQ==", - "dev": true, - "requires": { - "chalk": "^2.0.1" - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "ssri": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz", - "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==", - "dev": true, - "requires": { - "minipass": "^3.1.1" - } - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true - }, - "streamroller": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz", - "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==", - "dev": true, - "requires": { - "date-format": "^2.1.0", - "debug": "^4.1.1", - "fs-extra": "^8.1.0" - }, - "dependencies": { - "date-format": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz", - "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==", - "dev": true - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - } - } - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "style-loader": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", - "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", - "dev": true, - "requires": { - "loader-utils": "^2.0.0", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "stylehacks": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", - "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", - "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "stylus": { - "version": "0.54.8", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz", - "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==", - "dev": true, - "requires": { - "css-parse": "~2.0.0", - "debug": "~3.1.0", - "glob": "^7.1.6", - "mkdirp": "~1.0.4", - "safer-buffer": "^2.1.2", - "sax": "~1.2.4", - "semver": "^6.3.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "stylus-loader": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-4.3.1.tgz", - "integrity": "sha512-apDYJEM5ZpOAWbWInWcsbtI8gHNr/XYVcSY/tWqOUPt7M5tqhtwXVsAkgyiVjhuvw2Yrjq474a9H+g4d047Ebw==", - "dev": true, - "requires": { - "fast-glob": "^3.2.4", - "klona": "^2.0.4", - "loader-utils": "^2.0.0", - "normalize-path": "^3.0.0", - "schema-utils": "^3.0.0" - }, - "dependencies": { - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - } - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "svgo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", - "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.37", - "csso": "^4.0.2", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - } - }, - "symbol-observable": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-2.0.3.tgz", - "integrity": "sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==", - "dev": true - }, - "tapable": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", - "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", - "dev": true - }, - "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", - "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "terser": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.7.tgz", - "integrity": "sha512-lJbKdfxWvjpV330U4PBZStCT9h3N9A4zZVA5Y4k9sCWXknrpdyxi1oMsRKLmQ/YDMDxSBKIh88v0SkdhdqX06w==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" - } - }, - "terser-webpack-plugin": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz", - "integrity": "sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==", - "dev": true, - "requires": { - "cacache": "^15.0.5", - "find-cache-dir": "^3.3.1", - "jest-worker": "^26.5.0", - "p-limit": "^3.0.2", - "schema-utils": "^3.0.0", - "serialize-javascript": "^5.0.1", - "source-map": "^0.6.1", - "terser": "^5.3.4", - "webpack-sources": "^1.4.3" - }, - "dependencies": { - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "schema-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", - "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "timers-browserify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", - "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "dev": true - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "ts-node": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", - "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", - "dev": true, - "requires": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "^3.0.0" - } - }, - "ts-pnp": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", - "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==", - "dev": true - }, - "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - }, - "tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", - "dev": true - }, - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", - "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==", - "dev": true - }, - "ua-parser-js": { - "version": "0.7.21", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", - "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==", - "dev": true - }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "universal-analytics": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.23.tgz", - "integrity": "sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "request": "^2.88.2", - "uuid": "^3.0.0" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true - }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - }, - "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } - } - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validate-npm-package-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", - "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", - "dev": true, - "requires": { - "builtins": "^1.0.3" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true - }, - "vendors": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", - "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", - "dev": true - }, - "watchpack": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", - "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", - "dev": true, - "requires": { - "chokidar": "^3.4.1", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.1" - } - }, - "watchpack-chokidar2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", - "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", - "dev": true, - "optional": true, - "requires": { - "chokidar": "^2.1.8" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "optional": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "optional": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "optional": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "optional": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "requires": { - "minimalistic-assert": "^1.0.0" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "webdriver-js-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", - "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", - "dev": true, - "requires": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - } - }, - "webpack": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz", - "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/wasm-edit": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "acorn": "^6.4.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.3.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.3", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.7.4", - "webpack-sources": "^1.4.1" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", - "dev": true, - "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "enhanced-resolve": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", - "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true - }, - "terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - } - }, - "terser-webpack-plugin": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", - "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", - "dev": true, - "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "webpack-dev-middleware": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", - "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", - "dev": true, - "requires": { - "memory-fs": "^0.4.1", - "mime": "^2.4.4", - "mkdirp": "^0.5.1", - "range-parser": "^1.2.1", - "webpack-log": "^2.0.0" - }, - "dependencies": { - "mime": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz", - "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==", - "dev": true - } - } - }, - "webpack-dev-server": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", - "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", - "dev": true, - "requires": { - "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.1.8", - "compression": "^1.7.4", - "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", - "del": "^4.1.1", - "express": "^4.17.1", - "html-entities": "^1.3.1", - "http-proxy-middleware": "0.19.1", - "import-local": "^2.0.0", - "internal-ip": "^4.3.0", - "ip": "^1.1.5", - "is-absolute-url": "^3.0.3", - "killable": "^1.0.1", - "loglevel": "^1.6.8", - "opn": "^5.5.0", - "p-retry": "^3.0.1", - "portfinder": "^1.0.26", - "schema-utils": "^1.0.0", - "selfsigned": "^1.10.7", - "semver": "^6.3.0", - "serve-index": "^1.9.1", - "sockjs": "0.3.20", - "sockjs-client": "1.4.0", - "spdy": "^4.0.2", - "strip-ansi": "^3.0.1", - "supports-color": "^6.1.0", - "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.2", - "webpack-log": "^2.0.0", - "ws": "^6.2.1", - "yargs": "^13.3.2" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "webpack-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", - "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", - "dev": true, - "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "ansi-colors": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", - "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", - "dev": true - } - } - }, - "webpack-merge": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.2.0.tgz", - "integrity": "sha512-QBglJBg5+lItm3/Lopv8KDDK01+hjdg2azEwi/4vKJ8ZmGPdtJsTpjtNNOW3a4WiqzXdCATtTudOZJngE7RKkA==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - } - }, - "webpack-sources": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.0.1.tgz", - "integrity": "sha512-A9oYz7ANQBK5EN19rUXbvNgfdfZf5U2gP0769OXsj9CvYkCR6OHOsd6OKyEy4H38GGxpsQPKIL83NC64QY6Xmw==", - "dev": true, - "requires": { - "source-list-map": "^2.0.1", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "webpack-subresource-integrity": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.1.tgz", - "integrity": "sha512-uekbQ93PZ9e7BFB8Hl9cFIVYQyQqiXp2ExKk9Zv+qZfH/zHXHrCFAfw1VW0+NqWbTWrs/HnuDrto3+tiPXh//Q==", - "dev": true, - "requires": { - "webpack-sources": "^1.3.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - } - } - } - }, - "websocket-driver": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", - "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", - "dev": true, - "requires": { - "websocket-extensions": ">=0.1.1" - } - }, - "websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true - }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "worker-plugin": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-5.0.0.tgz", - "integrity": "sha512-AXMUstURCxDD6yGam2r4E34aJg6kW85IiaeX72hi+I1cxyaMUtrvVY6sbfpGKAj5e7f68Acl62BjQF5aOOx2IQ==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true - }, - "xmlhttprequest-ssl": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", - "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", - "dev": true - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } - } - }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - }, - "zone.js": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz", - "integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==" - } - } -} diff --git a/src/DevHive.Angular/package.json b/src/DevHive.Angular/package.json deleted file mode 100644 index 967483a..0000000 --- a/src/DevHive.Angular/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "angular", - "version": "0.0.0", - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build", - "test": "ng test", - "lint": "ng lint", - "e2e": "ng e2e" - }, - "private": true, - "dependencies": { - "@angular/animations": "^11.0.7", - "@angular/cdk": "~11.0.2-sha-c6231e19b", - "@angular/common": "^11.0.7", - "@angular/compiler": "^11.0.7", - "@angular/core": "^11.0.7", - "@angular/forms": "^11.0.7", - "@angular/material": "^11.0.3", - "@angular/platform-browser": "^11.0.7", - "@angular/platform-browser-dynamic": "^11.0.7", - "@angular/router": "^11.0.7", - "@types/jwt-decode": "^3.1.0", - "form-data": "^3.0.0", - "guid-typescript": "^1.0.9", - "jwt-decode": "^3.1.2", - "normalize.css": "^8.0.1", - "reset-css": "^5.0.1", - "rxjs": "~6.6.0", - "tslib": "^2.1.0", - "zone.js": "~0.10.2" - }, - "devDependencies": { - "@angular-devkit/build-angular": "^0.1100.6", - "@angular/cli": "^11.0.6", - "@angular/compiler-cli": "^11.0.7", - "@types/jasmine": "~3.6.0", - "@types/node": "^12.19.12", - "codelyzer": "^6.0.0", - "jasmine-core": "~3.6.0", - "jasmine-spec-reporter": "~5.0.0", - "karma": "~5.1.0", - "karma-chrome-launcher": "~3.1.0", - "karma-coverage": "~2.0.3", - "karma-jasmine": "~4.0.0", - "karma-jasmine-html-reporter": "^1.5.0", - "protractor": "~7.0.0", - "ts-node": "~8.3.0", - "tslint": "~6.1.0", - "typescript": "~4.0.2" - } -} diff --git a/src/DevHive.Angular/src/app/app-constants.module.ts b/src/DevHive.Angular/src/app/app-constants.module.ts deleted file mode 100644 index d72af53..0000000 --- a/src/DevHive.Angular/src/app/app-constants.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -export class AppConstants { - public static BASE_API_URL = 'http://localhost:5000/api'; - - public static API_USER_URL = AppConstants.BASE_API_URL + '/User'; - public static API_USER_LOGIN_URL = AppConstants.API_USER_URL + '/login'; - public static API_USER_REGISTER_URL = AppConstants.API_USER_URL + '/register'; - - public static API_LANGUAGE_URL = AppConstants.BASE_API_URL + '/Language'; - public static API_TECHNOLOGY_URL = AppConstants.BASE_API_URL + '/Technology'; - - public static API_POST_URL = AppConstants.BASE_API_URL + '/Post'; - public static API_FEED_URL = AppConstants.BASE_API_URL + '/Feed'; - public static API_COMMENT_URL = AppConstants.BASE_API_URL + '/Comment'; - - public static PAGE_SIZE = 10; - public static FALLBACK_PROFILE_ICON = 'assets/images/feed/profile-pic.png'; - - public static SESSION_TOKEN_KEY = 'UserCred'; - public static ADMIN_ROLE_NAME = 'Admin'; -} diff --git a/src/DevHive.Angular/src/app/app-routing.module.ts b/src/DevHive.Angular/src/app/app-routing.module.ts deleted file mode 100644 index 0d83079..0000000 --- a/src/DevHive.Angular/src/app/app-routing.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; -import { FeedComponent } from './components/feed/feed.component'; -import { LoginComponent } from './components/login/login.component'; -import { RegisterComponent } from './components/register/register.component'; -import { ProfileComponent } from './components/profile/profile.component'; -import { ProfileSettingsComponent } from './components/profile-settings/profile-settings.component'; -import { NotFoundComponent } from './components/not-found/not-found.component'; -import { PostPageComponent } from './components/post-page/post-page.component'; -import {AdminPanelPageComponent} from './components/admin-panel-page/admin-panel-page.component'; -import {CommentPageComponent} from './components/comment-page/comment-page.component'; - -const routes: Routes = [ - { path: '', component: FeedComponent }, - { path: 'login', component: LoginComponent }, - { path: 'register', component: RegisterComponent }, - { path: 'profile/:username', component: ProfileComponent }, - { path: 'profile/:username/settings', component: ProfileSettingsComponent }, - { path: 'post/:id', component: PostPageComponent }, - { path: 'comment/:id', component: CommentPageComponent }, - { path: 'admin-panel', component: AdminPanelPageComponent }, - { path: 'not-found', component: NotFoundComponent }, - { path: '**', component: NotFoundComponent } -]; - -@NgModule({ - imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] -}) -export class AppRoutingModule { } diff --git a/src/DevHive.Angular/src/app/app.component.css b/src/DevHive.Angular/src/app/app.component.css deleted file mode 100644 index aaebe5b..0000000 --- a/src/DevHive.Angular/src/app/app.component.css +++ /dev/null @@ -1,4 +0,0 @@ -.main { - width: 100%; - height: 100%; -} diff --git a/src/DevHive.Angular/src/app/app.component.html b/src/DevHive.Angular/src/app/app.component.html deleted file mode 100644 index 6c51026..0000000 --- a/src/DevHive.Angular/src/app/app.component.html +++ /dev/null @@ -1,3 +0,0 @@ -<div class="main"> - <router-outlet></router-outlet> -</div> diff --git a/src/DevHive.Angular/src/app/app.component.spec.ts b/src/DevHive.Angular/src/app/app.component.spec.ts deleted file mode 100644 index e4bf775..0000000 --- a/src/DevHive.Angular/src/app/app.component.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - RouterTestingModule - ], - declarations: [ - AppComponent - ], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it(`should have as title 'Angular'`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('Angular'); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement; - expect(compiled.querySelector('.content span').textContent).toContain('Angular app is running!'); - }); -}); diff --git a/src/DevHive.Angular/src/app/app.component.ts b/src/DevHive.Angular/src/app/app.component.ts deleted file mode 100644 index b9f46ae..0000000 --- a/src/DevHive.Angular/src/app/app.component.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.css'] -}) -export class AppComponent { - title = 'Angular'; -} diff --git a/src/DevHive.Angular/src/app/app.module.ts b/src/DevHive.Angular/src/app/app.module.ts deleted file mode 100644 index 9a2fafd..0000000 --- a/src/DevHive.Angular/src/app/app.module.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { MatInputModule } from '@angular/material/input'; -import { MatButtonModule } from '@angular/material/button'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { HttpClientModule } from '@angular/common/http'; - -import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; -import { LoginComponent } from './components/login/login.component'; -import { RegisterComponent } from './components/register/register.component'; -import { FeedComponent } from './components/feed/feed.component'; -import { PostComponent } from './components/post/post.component'; -import { ProfileComponent } from './components/profile/profile.component'; -import { ProfileSettingsComponent } from './components/profile-settings/profile-settings.component'; -import { NotFoundComponent } from './components/not-found/not-found.component'; -import { LoadingComponent } from './components/loading/loading.component'; -import { ErrorBarComponent } from './components/error-bar/error-bar.component'; -import { SuccessBarComponent } from './components/success-bar/success-bar.component'; -import { PostPageComponent } from './components/post-page/post-page.component'; -import { AdminPanelPageComponent } from './components/admin-panel-page/admin-panel-page.component'; -import { CommentComponent } from './components/comment/comment.component'; -import { CommentPageComponent } from './components/comment-page/comment-page.component'; -import { PostAttachmentComponent } from './components/post-attachment/post-attachment.component'; - -@NgModule({ - declarations: [ - AppComponent, - LoginComponent, - RegisterComponent, - FeedComponent, - PostComponent, - ProfileComponent, - ProfileSettingsComponent, - NotFoundComponent, - LoadingComponent, - ErrorBarComponent, - SuccessBarComponent, - PostPageComponent, - AdminPanelPageComponent, - CommentComponent, - CommentPageComponent, - PostAttachmentComponent - ], - imports: [ - BrowserModule, - AppRoutingModule, - ReactiveFormsModule, - BrowserAnimationsModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - HttpClientModule - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } diff --git a/src/DevHive.Angular/src/app/components/admin-panel-page/admin-panel-page.component.css b/src/DevHive.Angular/src/app/components/admin-panel-page/admin-panel-page.component.css deleted file mode 100644 index 1f98e20..0000000 --- a/src/DevHive.Angular/src/app/components/admin-panel-page/admin-panel-page.component.css +++ /dev/null @@ -1,42 +0,0 @@ -#content { - max-width: 22em; - justify-content: start; -} - -hr { - width: calc(100% - 1em); - color: black; - border: 1px solid black; -} - -#navigation { - width: 100%; - display: flex; -} - -#navigation > * { - flex: 1; - margin-left: .4em; -} - -.submit-btn:first-of-type { - margin-left: 0 !important; -} - -#all-languages, #all-technologies { - display: flex; - flex-wrap: wrap; -} - -.flexbox { - display: flex; -} - -.flexbox > * { - flex: 1; - margin-left: 1em; -} - -.flexbox > *:first-child { - margin-left: 0; -} diff --git a/src/DevHive.Angular/src/app/components/admin-panel-page/admin-panel-page.component.html b/src/DevHive.Angular/src/app/components/admin-panel-page/admin-panel-page.component.html deleted file mode 100644 index 980f12c..0000000 --- a/src/DevHive.Angular/src/app/components/admin-panel-page/admin-panel-page.component.html +++ /dev/null @@ -1,85 +0,0 @@ -<!-- <app-loading *ngIf="!dataArrived"></app-loading> --> - -<div id="content" *ngIf="!dataArrived"> - <nav id="navigation"> - <button class="submit-btn" (click)="backToProfile()">ᐊ Back to profile</button> - <button class="submit-btn" (click)="backToFeed()">ᐊ Back to feed</button> - <button class="submit-btn" (click)="logout()">Logout</button> - </nav> - <hr> - <div class="scroll-standalone"> - <app-success-bar></app-success-bar> - <app-error-bar></app-error-bar> - - <button type="button" class="submit-btn edit-btn" (click)="toggleLanguages()">▼ Edit Languages ▼</button> - <form [formGroup]="languageForm" (ngSubmit)="submitLanguages()" *ngIf="showLanguages"> - <div class="input-selection"> - <label>Create language:</label> - <input type="text" class="input-field" formControlName="languageCreate" placeholder="New language name"> - </div> - <label>Update language:</label> - <div class="flexbox input-selection"> - <input type="text" class="input-field" formControlName="updateLanguageOldName" placeholder="Old language name"> - <input type="text" class="input-field" formControlName="updateLanguageNewName" placeholder="New language name"> - </div> - <label>Delete language:</label> - <div class="flexbox input-selection"> - <input type="text" class="input-field" formControlName="deleteLanguageName" placeholder="Language name"> - </div> - <button class="submit-btn" type="submit">Modify languages</button> - <hr> - Available languages: - <div id="all-languages"> - <div class="user-language" *ngFor="let lang of availableLanguages"> - {{ lang.name }} - </div> - </div> - <hr> - </form> - - <button type="button" class="submit-btn edit-btn" (click)="toggleTechnologies()">▼ Edit Technologies ▼</button> - <form [formGroup]="technologyForm" (ngSubmit)="submitTechnologies()" *ngIf="showTechnologies"> - <div class="input-selection"> - <label>Create technology:</label> - <input type="text" class="input-field" formControlName="technologyCreate" placeholder="New technology name"> - </div> - <label>Update technology:</label> - <div class="flexbox input-selection"> - <input type="text" class="input-field" formControlName="updateTechnologyOldName" placeholder="Old technology name"> - <input type="text" class="input-field" formControlName="updateTechnologyNewName" placeholder="New technology name"> - </div> - <label>Delete technology:</label> - <div class="flexbox input-selection"> - <input type="text" class="input-field" formControlName="deleteTechnologyName" placeholder="Technology name"> - </div> - <button class="submit-btn" type="submit">Modify technologies</button> - <hr> - Available technologies: - <div id="all-technologies"> - <div class="user-technology" *ngFor="let tech of availableTechnologies"> - {{ tech.name }} - </div> - </div> - <hr> - </form> - - <button type="button" class="submit-btn delete-btn" (click)="toggleDeletions()">▼ Deletions ▼</button> - <form [formGroup]="deleteForm" (ngSubmit)="submitDelete()" *ngIf="showDeletions"> - <div class="input-selection"> - <label>Delete user by Id:</label> - <input type="text" class="input-field" formControlName="deleteUser" placeholder="User Id"> - </div> - - <div class="input-selection"> - <label>Delete post by Id:</label> - <input type="text" class="input-field" formControlName="deletePost" placeholder="Post Id"> - </div> - <div class="input-selection"> - <label>Delete comment by Id:</label> - <input type="text" class="input-field" formControlName="deleteComment" placeholder="Comment Id"> - </div> - <button class="submit-btn" type="submit">Delete</button> - <hr> - </form> - </div> -</div> diff --git a/src/DevHive.Angular/src/app/components/admin-panel-page/admin-panel-page.component.ts b/src/DevHive.Angular/src/app/components/admin-panel-page/admin-panel-page.component.ts deleted file mode 100644 index 99c0721..0000000 --- a/src/DevHive.Angular/src/app/components/admin-panel-page/admin-panel-page.component.ts +++ /dev/null @@ -1,334 +0,0 @@ -import { HttpErrorResponse } from '@angular/common/http'; -import { Component, OnInit, ViewChild } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; -import { Title } from '@angular/platform-browser'; -import { Router } from '@angular/router'; -import { Guid } from 'guid-typescript'; -import { AppConstants } from 'src/app/app-constants.module'; -import { CommentService } from 'src/app/services/comment.service'; -import { LanguageService } from 'src/app/services/language.service'; -import { PostService } from 'src/app/services/post.service'; -import { TechnologyService } from 'src/app/services/technology.service'; -import { TokenService } from 'src/app/services/token.service'; -import { UserService } from 'src/app/services/user.service'; -import { User } from 'src/models/identity/user'; -import { Language } from 'src/models/language'; -import { Technology } from 'src/models/technology'; -import { ErrorBarComponent } from '../error-bar/error-bar.component'; -import { SuccessBarComponent } from '../success-bar/success-bar.component'; - -@Component({ - selector: 'app-admin-panel-page', - templateUrl: './admin-panel-page.component.html', - styleUrls: ['./admin-panel-page.component.css'] -}) -export class AdminPanelPageComponent implements OnInit { - private _title = 'Admin Panel'; - @ViewChild(ErrorBarComponent) private _errorBar: ErrorBarComponent; - @ViewChild(SuccessBarComponent) private _successBar: SuccessBarComponent; - public dataArrived = false; - public showLanguages = false; - public showTechnologies = false; - public showDeletions = false; - public availableLanguages: Language[]; - public availableTechnologies: Technology[]; - public languageForm: FormGroup; - public technologyForm: FormGroup; - public deleteForm: FormGroup; - - constructor(private _titleService: Title, private _router: Router, private _fb: FormBuilder, private _userService: UserService, private _languageService: LanguageService, private _technologyService: TechnologyService, private _tokenService: TokenService, private _postService: PostService, private _commentService: CommentService) { - this._titleService.setTitle(this._title); - } - - ngOnInit(): void { - if (!this._tokenService.getTokenFromSessionStorage()) { - this._router.navigate(['/login']); - return; - } - - this._userService.getUserFromSessionStorageRequest().subscribe( - (result: object) => { - const user = result as User; - if (!user.roles.map(x => x.name).includes(AppConstants.ADMIN_ROLE_NAME)) { - this._router.navigate(['/login']); - } - }, - (err: HttpErrorResponse) => { - this._router.navigate(['/login']); - } - ); - - this.languageForm = this._fb.group({ - languageCreate: new FormControl(''), - updateLanguageOldName: new FormControl(''), - updateLanguageNewName: new FormControl(''), - deleteLanguageName: new FormControl('') - }); - - this.languageForm.valueChanges.subscribe(() => { - this._successBar?.hideMsg(); - this._errorBar?.hideError(); - }); - - this.technologyForm = this._fb.group({ - technologyCreate: new FormControl(''), - updateTechnologyOldName: new FormControl(''), - updateTechnologyNewName: new FormControl(''), - deleteTechnologyName: new FormControl('') - }); - - this.technologyForm.valueChanges.subscribe(() => { - this._successBar?.hideMsg(); - this._errorBar?.hideError(); - }); - - this.deleteForm = this._fb.group({ - deleteUser: new FormControl(''), - deletePost: new FormControl(''), - deleteComment: new FormControl('') - }); - - this.deleteForm.valueChanges.subscribe(() => { - this._successBar?.hideMsg(); - this._errorBar?.hideError(); - }); - - this.loadAvailableLanguages(); - this.loadAvailableTechnologies(); - } - - // Navigation - - backToProfile(): void { - this._router.navigate(['/profile/' + this._tokenService.getUsernameFromSessionStorageToken()]); - } - - backToFeed(): void { - this._router.navigate(['/']); - } - - logout(): void { - this._tokenService.logoutUserFromSessionStorage(); - this._router.navigate(['/login']); - } - - // Language modifying - - toggleLanguages(): void { - this.showLanguages = !this.showLanguages; - } - - submitLanguages(): void { - this.tryCreateLanguage(); - this.tryUpdateLanguage(); - this.tryDeleteLanguage(); - } - - private tryCreateLanguage(): void { - const languageCreate: string = this.languageForm.get('languageCreate')?.value; - - if (languageCreate !== '' && languageCreate !== null) { - this._languageService.createLanguageWithSessionStorageRequest(languageCreate.trim()).subscribe( - (result: object) => { - this.languageModifiedSuccess('Successfully updated languages!'); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - } - - private tryUpdateLanguage(): void { - const updateLanguageOldName: string = this.languageForm.get('updateLanguageOldName')?.value; - const updateLanguageNewName: string = this.languageForm.get('updateLanguageNewName')?.value; - - if (updateLanguageOldName !== '' && updateLanguageOldName !== null && updateLanguageNewName !== '' && updateLanguageNewName !== null) { - const langId = this.availableLanguages.filter(x => x.name === updateLanguageOldName.trim())[0].id; - - this._languageService.putLanguageWithSessionStorageRequest(langId, updateLanguageNewName.trim()).subscribe( - (result: object) => { - this.languageModifiedSuccess('Successfully updated languages!'); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - } - - private tryDeleteLanguage(): void { - const deleteLanguageName: string = this.languageForm.get('deleteLanguageName')?.value; - - if (deleteLanguageName !== '' && deleteLanguageName !== null) { - const langId = this.availableLanguages.filter(x => x.name === deleteLanguageName.trim())[0].id; - - this._languageService.deleteLanguageWithSessionStorageRequest(langId).subscribe( - (result: object) => { - this.languageModifiedSuccess('Successfully deleted language!'); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - } - - private languageModifiedSuccess(successMsg: string): void { - this.languageForm.reset(); - this._successBar.showMsg(successMsg); - this.loadAvailableLanguages(); - } - - private loadAvailableLanguages(): void { - this._languageService.getAllLanguagesWithSessionStorageRequest().subscribe( - (result: object) => { - this.availableLanguages = result as Language[]; - } - ); - } - - // Technology modifying - - toggleTechnologies(): void { - this.showTechnologies = !this.showTechnologies; - } - - submitTechnologies(): void { - this.tryCreateTechnology(); - this.tryUpdateTechnology(); - this.tryDeleteTechnology(); - } - - private tryCreateTechnology(): void { - const technologyCreate: string = this.technologyForm.get('technologyCreate')?.value; - - if (technologyCreate !== '' && technologyCreate !== null) { - this._technologyService.createTechnologyWithSessionStorageRequest(technologyCreate.trim()).subscribe( - (result: object) => { - this.technologyModifiedSuccess('Successfully updated technologies!'); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - } - - private tryUpdateTechnology(): void { - const updateTechnologyOldName: string = this.technologyForm.get('updateTechnologyOldName')?.value; - const updateTechnologyNewName: string = this.technologyForm.get('updateTechnologyNewName')?.value; - - if (updateTechnologyOldName !== '' && updateTechnologyOldName !== null && updateTechnologyNewName !== '' && updateTechnologyNewName !== null) { - const techId = this.availableTechnologies.filter(x => x.name === updateTechnologyOldName.trim())[0].id; - - this._technologyService.putTechnologyWithSessionStorageRequest(techId, updateTechnologyNewName.trim()).subscribe( - (result: object) => { - this.technologyModifiedSuccess('Successfully updated technologies!'); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - } - - private tryDeleteTechnology(): void { - const deleteTechnologyName: string = this.technologyForm.get('deleteTechnologyName')?.value; - - if (deleteTechnologyName !== '' && deleteTechnologyName !== null) { - const techId = this.availableTechnologies.filter(x => x.name === deleteTechnologyName.trim())[0].id; - - this._technologyService.deleteTechnologyWithSessionStorageRequest(techId).subscribe( - (result: object) => { - this.technologyModifiedSuccess('Successfully deleted technology!'); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - } - - private technologyModifiedSuccess(successMsg: string): void { - this.technologyForm.reset(); - this._successBar.showMsg(successMsg); - this.loadAvailableTechnologies(); - } - - private loadAvailableTechnologies(): void { - this._technologyService.getAllTechnologiesWithSessionStorageRequest().subscribe( - (result: object) => { - this.availableTechnologies = result as Technology[]; - } - ); - } - - // Deletions - - toggleDeletions(): void { - this.showDeletions = !this.showDeletions; - } - - submitDelete(): void { - this.tryDeleteUser(); - this.tryDeletePost(); - this.tryDeleteComment(); - } - - private tryDeleteUser(): void { - const deleteUser: string = this.deleteForm.get('deleteUser')?.value; - - if (deleteUser !== '' && deleteUser !== null) { - const userId: Guid = Guid.parse(deleteUser); - - this._userService.deleteUserRequest(userId, this._tokenService.getTokenFromSessionStorage()).subscribe( - (result: object) => { - this.deletionSuccess('Successfully deleted user!'); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - } - - private tryDeletePost(): void { - const deletePost: string = this.deleteForm.get('deletePost')?.value; - - if (deletePost !== '' && deletePost !== null) { - const postId: Guid = Guid.parse(deletePost); - - this._postService.deletePostRequest(postId, this._tokenService.getTokenFromSessionStorage()).subscribe( - (result: object) => { - this.deletionSuccess('Successfully deleted user!'); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - } - - private tryDeleteComment(): void { - const deleteComment: string = this.deleteForm.get('deleteComment')?.value; - - if (deleteComment !== '' && deleteComment !== null) { - const commentId: Guid = Guid.parse(deleteComment); - - this._commentService.deleteCommentWithSessionStorage(commentId).subscribe( - (result: object) => { - this.deletionSuccess('Successfully deleted comment!'); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - } - - private deletionSuccess(successMsg: string): void { - this.deleteForm.reset(); - this._successBar.showMsg(successMsg); - } -} diff --git a/src/DevHive.Angular/src/app/components/comment-page/comment-page.component.css b/src/DevHive.Angular/src/app/components/comment-page/comment-page.component.css deleted file mode 100644 index b886bc1..0000000 --- a/src/DevHive.Angular/src/app/components/comment-page/comment-page.component.css +++ /dev/null @@ -1,27 +0,0 @@ -#content { - justify-content: flex-start !important; -} - -#content > * { - width: 100%; -} - -.many-buttons { - width: 100%; - display: flex; -} - -.many-buttons > * { - flex: 1; - margin-right: .3em; -} - -.many-buttons > *:last-of-type { - margin-right: 0; -} - -.submit-btn { - max-width: 98%; - margin: 0 auto; - margin-bottom: .5em; -} diff --git a/src/DevHive.Angular/src/app/components/comment-page/comment-page.component.html b/src/DevHive.Angular/src/app/components/comment-page/comment-page.component.html deleted file mode 100644 index 2d110d6..0000000 --- a/src/DevHive.Angular/src/app/components/comment-page/comment-page.component.html +++ /dev/null @@ -1,14 +0,0 @@ -<app-loading *ngIf="!loaded"></app-loading> - -<div id="content" *ngIf="loaded"> - <button class="submit-btn" type="submit" (click)="toPost()">ᐊ Back to post</button> - <app-comment [paramId]="commentId.toString()"></app-comment> - <div class="many-buttons" *ngIf="editable"> - <button class="submit-btn" (click)="editComment()">Edit comment</button> - <button class="submit-btn delete-btn" (click)="deleteComment()">Delete comment</button> - </div> - <form [formGroup]="editCommentFormGroup" (ngSubmit)="editComment()"> - <input type="text" *ngIf="editingComment" placeholder="New comment message" class="input-field" formControlName="newCommentMessage"> - <input type="submit" style="display: none" /> - </form> -<div> diff --git a/src/DevHive.Angular/src/app/components/comment-page/comment-page.component.ts b/src/DevHive.Angular/src/app/components/comment-page/comment-page.component.ts deleted file mode 100644 index bd4cfe5..0000000 --- a/src/DevHive.Angular/src/app/components/comment-page/comment-page.component.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { HttpErrorResponse } from '@angular/common/http'; -import { Component, OnInit } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; -import { Title } from '@angular/platform-browser'; -import { Router } from '@angular/router'; -import { Guid } from 'guid-typescript'; -import { CommentService } from 'src/app/services/comment.service'; -import { TokenService } from 'src/app/services/token.service'; -import { Comment } from 'src/models/comment'; - -@Component({ - selector: 'app-comment-page', - templateUrl: './comment-page.component.html', - styleUrls: ['./comment-page.component.css'] -}) -export class CommentPageComponent implements OnInit { - private _title = 'Comment'; - public loaded = false; - public loggedIn = false; - public editable = false; - public editingComment = false; - public commentId: Guid; - public comment: Comment; - public editCommentFormGroup: FormGroup; - - constructor(private _titleService: Title, private _router: Router, private _fb: FormBuilder, private _tokenService: TokenService, private _commentService: CommentService){ - this._titleService.setTitle(this._title); - } - - ngOnInit(): void { - this.loggedIn = this._tokenService.getTokenFromSessionStorage() !== ''; - this.commentId = Guid.parse(this._router.url.substring(9)); - - // Gets the post and the logged in user and compares them, - // to determine if the current post is made by the user - this._commentService.getCommentRequest(this.commentId).subscribe( - (result: object) => { - this.comment = result as Comment; - if (this.loggedIn) { - this.editable = this.comment.issuerUsername === this._tokenService.getUsernameFromSessionStorageToken(); - } - this.loaded = true; - }, - (err: HttpErrorResponse) => { - this._router.navigate(['/not-found']); - } - ); - - this.editCommentFormGroup = this._fb.group({ - newCommentMessage: new FormControl('') - }); - } - - toPost(): void { - this._router.navigate(['/post/' + this.comment.postId]); - } - - editComment(): void { - if (this._tokenService.getTokenFromSessionStorage() === '') { - this._router.navigate(['/login']); - return; - } - - if (this.editingComment) { - const newMessage = this.editCommentFormGroup.get('newCommentMessage')?.value; - if (newMessage !== '') { - console.log(this.commentId); - this._commentService.putCommentWithSessionStorageRequest(this.commentId, this.comment.postId, newMessage).subscribe( - (result: object) => { - this.reloadPage(); - } - ); - } - } - this.editingComment = !this.editingComment; - } - - deleteComment(): void { - this._commentService.deleteCommentWithSessionStorage(this.commentId).subscribe( - (result: object) => { - this.toPost(); - } - ); - } - - private reloadPage(): void { - this._router.routeReuseStrategy.shouldReuseRoute = () => false; - this._router.onSameUrlNavigation = 'reload'; - this._router.navigate([this._router.url]); - } -} diff --git a/src/DevHive.Angular/src/app/components/comment/comment.component.css b/src/DevHive.Angular/src/app/components/comment/comment.component.css deleted file mode 100644 index d82f10e..0000000 --- a/src/DevHive.Angular/src/app/components/comment/comment.component.css +++ /dev/null @@ -1,59 +0,0 @@ -.comment { - display: flex; - width: 100%; - - margin: .5em auto; - box-sizing: border-box; - padding: .5em; - background-color: var(--card-bg); -} - -.comment:first-child { - margin-top: 0; -} - -/* Author */ - -.author { - display: flex; - margin-bottom: .2em; -} - -.author:hover { - cursor: pointer; -} - -.author > img { - width: 1.7em; - height: 1.7em; - margin-right: .2em; -} - -.author-info > .name { - font-size: .8em; -} - -.author-info > .handle { - font-size: .6em; - color: gray; -} - -/* Content */ - -.content { - flex: 1; -} - -.message { - margin: .3em 0; - word-break: break-all; -} - -.timestamp { - font-size: .5em; - color: gray; -} - -.message:hover, .timestamp:hover { - cursor: pointer; -} diff --git a/src/DevHive.Angular/src/app/components/comment/comment.component.html b/src/DevHive.Angular/src/app/components/comment/comment.component.html deleted file mode 100644 index 718e25c..0000000 --- a/src/DevHive.Angular/src/app/components/comment/comment.component.html +++ /dev/null @@ -1,23 +0,0 @@ -<app-loading *ngIf="!loaded"></app-loading> - -<div class="comment rounded-border" *ngIf="loaded"> - <div class="content"> - <div class="author" (click)="goToAuthorProfile()"> - <img class="round-image" [src]="user.profilePictureURL"> - <div class="author-info"> - <div class="name"> - {{ user.firstName }} {{ user.lastName }} - </div> - <div class="handle"> - @{{ user.userName }} - </div> - </div> - </div> - <div class="message" (click)="goToCommentPage()"> - {{ comment.message }} - </div> - <div class="timestamp" (click)="goToCommentPage()"> - {{ timeCreated }} - </div> - </div> -</div> diff --git a/src/DevHive.Angular/src/app/components/comment/comment.component.ts b/src/DevHive.Angular/src/app/components/comment/comment.component.ts deleted file mode 100644 index 5076769..0000000 --- a/src/DevHive.Angular/src/app/components/comment/comment.component.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { Guid } from 'guid-typescript'; -import { CommentService } from 'src/app/services/comment.service'; -import { UserService } from 'src/app/services/user.service'; -import { Comment } from 'src/models/comment'; -import { User } from 'src/models/identity/user'; - -@Component({ - selector: 'app-comment', - templateUrl: './comment.component.html', - styleUrls: ['./comment.component.css'] -}) -export class CommentComponent implements OnInit { - public loaded = false; - public user: User; - public comment: Comment; - public timeCreated: string; - @Input() paramId: string; - - constructor(private _router: Router, private _commentService: CommentService, private _userService: UserService) - { } - - ngOnInit(): void { - this.comment = this._commentService.getDefaultComment(); - this.user = this._userService.getDefaultUser(); - - this._commentService.getCommentRequest(Guid.parse(this.paramId)).subscribe( - (result: object) => { - Object.assign(this.comment, result); - - this.timeCreated = new Date(this.comment.timeCreated).toLocaleString('en-GB'); - this.loadUser(); - } - ); - } - - private loadUser(): void { - this._userService.getUserByUsernameRequest(this.comment.issuerUsername).subscribe( - (result: object) => { - Object.assign(this.user, result); - this.loaded = true; - } - ); - } - - goToAuthorProfile(): void { - this._router.navigate(['/profile/' + this.comment.issuerUsername]); - } - - goToCommentPage(): void { - this._router.navigate(['/comment/' + this.comment.commentId]); - } -} diff --git a/src/DevHive.Angular/src/app/components/error-bar/error-bar.component.css b/src/DevHive.Angular/src/app/components/error-bar/error-bar.component.css deleted file mode 100644 index 8f8edd9..0000000 --- a/src/DevHive.Angular/src/app/components/error-bar/error-bar.component.css +++ /dev/null @@ -1,12 +0,0 @@ -#error-bar { - box-sizing: border-box; - width: 100%; - background-color: var(--failure); - color: white; - padding: .2em; - text-align: center; -} - -#error-bar:empty { - display: none; -} diff --git a/src/DevHive.Angular/src/app/components/error-bar/error-bar.component.html b/src/DevHive.Angular/src/app/components/error-bar/error-bar.component.html deleted file mode 100644 index f1995ab..0000000 --- a/src/DevHive.Angular/src/app/components/error-bar/error-bar.component.html +++ /dev/null @@ -1 +0,0 @@ -<div id="error-bar">{{errorMsg}}</div> diff --git a/src/DevHive.Angular/src/app/components/error-bar/error-bar.component.ts b/src/DevHive.Angular/src/app/components/error-bar/error-bar.component.ts deleted file mode 100644 index 111bac8..0000000 --- a/src/DevHive.Angular/src/app/components/error-bar/error-bar.component.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { HttpErrorResponse } from '@angular/common/http'; -import { Component, OnInit } from '@angular/core'; -import { IApiError } from 'src/interfaces/api-error'; - -@Component({ - selector: 'app-error-bar', - templateUrl: './error-bar.component.html', - styleUrls: ['./error-bar.component.css'] -}) -export class ErrorBarComponent implements OnInit { - public errorMsg = ''; - - constructor() - { } - - ngOnInit(): void { - this.hideError(); - } - - showError(error: HttpErrorResponse): void { - const test: IApiError = { - type: '', - title: 'Error!', - status: 0, - traceId: '' - }; - Object.assign(test, error.error); - this.errorMsg = test.title; - } - - hideError(): void { - this.errorMsg = ''; - } -} diff --git a/src/DevHive.Angular/src/app/components/feed/feed.component.css b/src/DevHive.Angular/src/app/components/feed/feed.component.css deleted file mode 100644 index cb155c6..0000000 --- a/src/DevHive.Angular/src/app/components/feed/feed.component.css +++ /dev/null @@ -1,179 +0,0 @@ -#feed-page { - height: 100%; - max-width: 40em; - box-sizing: border-box; - border: .5em solid var(--bg-color); - - margin: 0 auto; - - display: flex; -} - -@media screen and (max-width: 750px) { - #profile-bar { - display: none !important; - } - #top-bar-profile-pic { - display: initial; - } -} -@media screen and (min-width: 750px) { - #profile-bar { - display: initial; - } - #top-bar-profile-pic { - display: none !important; - } -} - -/* Content */ - -#feed-content { - flex: 1; - display: flex; - flex-direction: column; -} - -/* Profile bar */ - -#profile-bar { - width: 20%; - height: fit-content; - display: flex; - flex-direction: column; - align-items: center; - - box-sizing: border-box; - background-color: var(--bg-color); - - margin-right: .5em; - padding-bottom: 1em; - padding: .5em; - border-radius: .6em; -} - -#profile-bar-profile-pic { - width: 7em; - height: 7em; - box-sizing: border-box; - padding: .5em; -} - -#profile-bar-name { - text-align: center; -} - -#profile-bar-username { - margin: .5em 0; -} - -#profile-bar > #profile-info { - display: flex; - flex-direction: column; - align-items: center; -} - -/* Top bar */ - -#top-bar { - display: flex; - flex-direction: column; - width: 98%; - margin: 0 auto; - margin-bottom: .5em; - box-sizing: border-box; -} - -#top-bar-profile-pic { - height: 2.5em; - width: 2.5em; - margin-right: .5em; -} - -#top-bar-profile-pic > img { - height: inherit; - width: inherit; -} - -#top-bar-open-chat { - /* Until implemented */ - display: none; -} - -#main-content { - display: flex; -} - -/* Create post */ - -#create-post-form { - width: 100%; - position: relative; - display: flex; - flex-direction: column; - box-sizing: border-box; -} - -#form-inputs { - display: flex; -} - -#top-bar-create-post { - flex: 1; - font-size: inherit; - width: 100%; - height: 100%; - margin: 0 auto; - box-sizing: border-box; - border: 2px solid var(--bg-color); - border-radius: .6em; -} - -#file-upload { - font-size: inherit; - color: transparent; - width: 1.5em; - height: 1.5em; -} - -#file-upload:hover { - cursor: pointer; -} - -#file-upload::-webkit-file-upload-button { - visibility: hidden; -} - -#attachment-img { - height: 1.5em; - width: 1.5em; - position: absolute; - right: .4em; - pointer-events: none; -} - -/* Posts */ - -#no-posts-msg { - width: 100%; - margin-top: 1em; - color: gray; - text-align: center; -} - -/* Elements, that act as buttons */ - -#profile-bar > #profile-info:hover, -#top-bar-profile-pic:hover { - cursor: pointer; -} - -/* Can't copy text from */ - -#profile-bar, -.vote { - -webkit-user-select: none; /* Safari */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* IE10+/Edge */ - user-select: none; /* Standard */ -} diff --git a/src/DevHive.Angular/src/app/components/feed/feed.component.html b/src/DevHive.Angular/src/app/components/feed/feed.component.html deleted file mode 100644 index 1a03dcc..0000000 --- a/src/DevHive.Angular/src/app/components/feed/feed.component.html +++ /dev/null @@ -1,52 +0,0 @@ -<app-loading *ngIf="!dataArrived"></app-loading> - -<div id="feed-page" *ngIf="dataArrived"> - <nav id="profile-bar" class="round-image rounded-border"> - <div id="profile-info" (click)="goToProfile()"> - <img id="profile-bar-profile-pic" class="round-image" [src]="user.profilePictureURL" alt=""/> - <div id="profile-bar-name"> - {{ user.firstName }} {{ user.lastName }} - </div> - <div id="profile-bar-username"> - @{{ user.userName }} - </div> - </div> - <button class="submit-btn" (click)="goToSettings()">Settings</button> - <button class="submit-btn" (click)="logout()">Logout</button> - </nav> - <div id="feed-content"> - <nav id="top-bar"> - <div id="main-content"> - <img id="top-bar-profile-pic" class="round-image" [src]="user.profilePictureURL" alt="" (click)="goToProfile()"> - <form id="create-post-form" class="rounded-border" [formGroup]="createPostFormGroup" (ngSubmit)="createPost()"> - <div id="form-inputs"> - <input id="top-bar-create-post" type="text" formControlName="newPostMessage" placeholder="What's on your mind?"/> - <input type="submit" style="display: none" /> <!-- You need this element, so when you press enter the request is sent --> - <img id="attachment-img" src="assets/images/paper-clip.png"> - <input id="file-upload" type="file" formControlName="fileUpload" (change)="onFileUpload($event)" multiple> - </div> - <div class="form-attachments"> - <div *ngFor="let file of files" class="form-attachment"> - {{ file.name ? file.name : 'Attachment' }} - <div class="remove-form-attachment" (click)="removeAttachment(file.name)"> - ☒ - </div> - </div> - </div> - </form> - <a id="top-bar-open-chat" href=""> - <img src="assets/images/feed/chat-pic.png" alt=""/> - </a> - </div> - </nav> - <div id="posts" class="scroll-standalone" (scroll)="onScroll($event)"> - <div id="no-posts-msg" *ngIf="posts.length === 0"> - None of your friends have posted anything yet!<br> - Try refreshing your page! - </div> - <div *ngFor="let friendPost of posts" class="post"> - <app-post [paramId]="friendPost.postId.toString()"></app-post> - </div> - </div> - </div> -</div> diff --git a/src/DevHive.Angular/src/app/components/feed/feed.component.ts b/src/DevHive.Angular/src/app/components/feed/feed.component.ts deleted file mode 100644 index b412b3c..0000000 --- a/src/DevHive.Angular/src/app/components/feed/feed.component.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Title } from '@angular/platform-browser'; -import { Router } from '@angular/router'; -import { User } from 'src/models/identity/user'; -import { UserService } from '../../services/user.service'; -import { AppConstants } from 'src/app/app-constants.module'; -import { HttpErrorResponse } from '@angular/common/http'; -import { FeedService } from 'src/app/services/feed.service'; -import { Post } from 'src/models/post'; -import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; -import { PostService } from 'src/app/services/post.service'; -import { TokenService } from 'src/app/services/token.service'; - -@Component({ - selector: 'app-feed', - templateUrl: './feed.component.html', - styleUrls: ['./feed.component.css'] -}) -export class FeedComponent implements OnInit { - private _title = 'Feed'; - private _timeLoaded: string; // we send the time to the api as a string - private _currentPage: number; - public dataArrived = false; - public user: User; - public posts: Post[]; - public createPostFormGroup: FormGroup; - public files: File[]; - - constructor(private _titleService: Title, private _fb: FormBuilder, private _router: Router, private _userService: UserService, private _feedService: FeedService, private _postService: PostService, private _tokenService: TokenService) { - this._titleService.setTitle(this._title); - } - - ngOnInit(): void { - if (!this._tokenService.getTokenFromSessionStorage()) { - this._router.navigate(['/login']); - return; - } - - this._currentPage = 1; - this.posts = []; - this.user = this._userService.getDefaultUser(); - this.files = []; - - const now = new Date(); - now.setHours(now.getHours() + 2); // accounting for eastern european timezone - this._timeLoaded = now.toISOString(); - - this.createPostFormGroup = this._fb.group({ - newPostMessage: new FormControl(''), - fileUpload: new FormControl('') - }); - - this._userService.getUserFromSessionStorageRequest().subscribe( - (res: object) => { - Object.assign(this.user, res); - this.loadFeed(); - }, - (err: HttpErrorResponse) => { - this.logout(); - } - ); - } - - private loadFeed(): void { - this._feedService.getUserFeedFromSessionStorageRequest(this._currentPage++, this._timeLoaded, AppConstants.PAGE_SIZE).subscribe( - (result: object) => { - this.posts.push(...Object.values(result)[0]); - this.finishUserLoading(); - }, - (err) => { - this.finishUserLoading(); - } - ); - } - - private finishUserLoading(): void { - this.dataArrived = true; - } - - goToProfile(): void { - this._router.navigate(['/profile/' + this.user.userName]); - } - - goToSettings(): void { - this._router.navigate(['/profile/' + this.user.userName + '/settings']); - } - - logout(): void { - this._tokenService.logoutUserFromSessionStorage(); - this._router.navigate(['/login']); - } - - onFileUpload(event: any): void { - this.files.push(...event.target.files); - this.createPostFormGroup.get('fileUpload')?.reset(); - } - - removeAttachment(fileName: string): void { - this.files = this.files.filter(x => x.name !== fileName); - } - - createPost(): void { - const postMessage = this.createPostFormGroup.get('newPostMessage')?.value; - this.dataArrived = false; - - this._postService.createPostWithSessionStorageRequest(postMessage, this.files).subscribe( - (result: object) => { - this.goToProfile(); - }, - (err: HttpErrorResponse) => { - this.dataArrived = true; - } - ); - } - - onScroll(event: any): void { - // Detects when the element has reached the bottom, thx https://stackoverflow.com/a/50038429/12036073 - if (event.target.offsetHeight + event.target.scrollTop >= event.target.scrollHeight && this._currentPage > 0) { - this.loadFeed(); - } - } -} diff --git a/src/DevHive.Angular/src/app/components/kaleidoscope/kaleidoscope.component.css b/src/DevHive.Angular/src/app/components/kaleidoscope/kaleidoscope.component.css deleted file mode 100644 index e69de29..0000000 --- a/src/DevHive.Angular/src/app/components/kaleidoscope/kaleidoscope.component.css +++ /dev/null diff --git a/src/DevHive.Angular/src/app/components/kaleidoscope/kaleidoscope.component.html b/src/DevHive.Angular/src/app/components/kaleidoscope/kaleidoscope.component.html deleted file mode 100644 index 7392a21..0000000 --- a/src/DevHive.Angular/src/app/components/kaleidoscope/kaleidoscope.component.html +++ /dev/null @@ -1,8 +0,0 @@ -<div class="kaleidoscope"> - <ng-container *ngComponentOutlet="component"></ng-container> - <app-login *ngIf="logged!"></app-login> - <app-register *ngIf="!logged!"></app-register> - <!-- <app-feed></app-feed> --> - <script>var logged = false;</script> - <!-- to be fixed tomorow --> -</div>
\ No newline at end of file diff --git a/src/DevHive.Angular/src/app/components/kaleidoscope/kaleidoscope.component.ts b/src/DevHive.Angular/src/app/components/kaleidoscope/kaleidoscope.component.ts deleted file mode 100644 index 1c5cda1..0000000 --- a/src/DevHive.Angular/src/app/components/kaleidoscope/kaleidoscope.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { LoginComponent } from '../login/login.component'; - -@Component({ - selector: 'app-kaleidoscope', - templateUrl: './kaleidoscope.component.html', - styleUrls: ['./kaleidoscope.component.css'] -}) -export class KaleidoscopeComponent implements OnInit { - - public _component: Component; - - constructor(loginComponent: LoginComponent) { - this._component = loginComponent as Component; - } - - ngOnInit(): void { - - } - - assignComponent(component: Component) { - this._component = component; - } -} diff --git a/src/DevHive.Angular/src/app/components/loading/loading.component.css b/src/DevHive.Angular/src/app/components/loading/loading.component.css deleted file mode 100644 index e69de29..0000000 --- a/src/DevHive.Angular/src/app/components/loading/loading.component.css +++ /dev/null diff --git a/src/DevHive.Angular/src/app/components/loading/loading.component.html b/src/DevHive.Angular/src/app/components/loading/loading.component.html deleted file mode 100644 index 8440f4e..0000000 --- a/src/DevHive.Angular/src/app/components/loading/loading.component.html +++ /dev/null @@ -1,3 +0,0 @@ -<div id="content"> - Loading... -</div> diff --git a/src/DevHive.Angular/src/app/components/loading/loading.component.ts b/src/DevHive.Angular/src/app/components/loading/loading.component.ts deleted file mode 100644 index e203484..0000000 --- a/src/DevHive.Angular/src/app/components/loading/loading.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-loading', - templateUrl: './loading.component.html', - styleUrls: ['./loading.component.css'] -}) -export class LoadingComponent implements OnInit { - - constructor() - { } - - ngOnInit(): void { - } -} diff --git a/src/DevHive.Angular/src/app/components/login/login.component.css b/src/DevHive.Angular/src/app/components/login/login.component.css deleted file mode 100644 index 766522e..0000000 --- a/src/DevHive.Angular/src/app/components/login/login.component.css +++ /dev/null @@ -1,32 +0,0 @@ -* { - transition: .2s; -} - -form { - width: 100%; -} - -#content hr { - width: 100%; - border: 1px solid black; - box-sizing: border-box; -} - -.input-selection:nth-of-type(1) { - margin-top: 1.2em; -} - -.submit-btn { - margin-bottom: .2em; -} - -.redirect-to-register { - color: var(--focus-color); - background-color: var(--bg-color); - border-color: var(--focus-color); -} - -.redirect-to-register:hover { - border-color: black !important; - color: black; -} diff --git a/src/DevHive.Angular/src/app/components/login/login.component.html b/src/DevHive.Angular/src/app/components/login/login.component.html deleted file mode 100644 index 13f9bbf..0000000 --- a/src/DevHive.Angular/src/app/components/login/login.component.html +++ /dev/null @@ -1,30 +0,0 @@ -<div id="content"> - <div class="title">Login</div> - - <form [formGroup]="loginUserFormGroup" (ngSubmit)="onSubmit()"> - <hr> - - <div class="input-selection"> - <input type="text" placeholder="Username" class="input-field" formControlName="username" required> - <label class="input-field-label">Username</label> - - <div class="input-errors"> - <label *ngIf="loginUserFormGroup.get('username')?.errors?.required" class="error">*Required</label> - </div> - </div> - - <div class="input-selection"> - <input type="password" placeholder="Password" class="input-field" formControlName="password" required> - <label class="input-field-label">Password</label> - - <div class="input-errors"> - <label *ngIf="loginUserFormGroup.get('password')?.errors?.required" class="error">*Required</label> - </div> - </div> - - <hr> - <button class="submit-btn" type="submit">Submit</button> - <app-error-bar></app-error-bar> - </form> - <button class="submit-btn redirect-to-register" (click)="onRedirectRegister()">New to DevHive? Register instead</button> -</div> diff --git a/src/DevHive.Angular/src/app/components/login/login.component.ts b/src/DevHive.Angular/src/app/components/login/login.component.ts deleted file mode 100644 index c3fb79c..0000000 --- a/src/DevHive.Angular/src/app/components/login/login.component.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; -import { FormGroup, FormBuilder, Validators, FormControl, AbstractControl } from '@angular/forms'; -import { Router } from '@angular/router'; -import { Title } from '@angular/platform-browser'; -import { UserService } from 'src/app/services/user.service'; -import { HttpErrorResponse } from '@angular/common/http'; -import { ErrorBarComponent } from '../error-bar/error-bar.component'; -import { TokenService } from 'src/app/services/token.service'; - -@Component({ - selector: 'app-login', - templateUrl: './login.component.html', - styleUrls: ['./login.component.css'] -}) -export class LoginComponent implements OnInit { - @ViewChild(ErrorBarComponent) private _errorBar: ErrorBarComponent; - private _title = 'Login'; - public loginUserFormGroup: FormGroup; - - constructor(private _titleService: Title, private _fb: FormBuilder, private _router: Router, private _userService: UserService, private _tokenService: TokenService) { - this._titleService.setTitle(this._title); - } - - ngOnInit(): void { - this.loginUserFormGroup = this._fb.group({ - username: new FormControl('', [ - Validators.required - ]), - password: new FormControl('', [ - Validators.required - ]) - }); - } - - onSubmit(): void { - this._errorBar.hideError(); - this._userService.loginUserRequest(this.loginUserFormGroup).subscribe( - (res: object) => { - this._tokenService.setUserTokenToSessionStorage(res); - this._router.navigate(['/']); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - - onRedirectRegister(): void { - this._router.navigate(['/register']); - } - - get username(): AbstractControl | null { - return this.loginUserFormGroup.get('username'); - } - - get password(): AbstractControl | null { - return this.loginUserFormGroup.get('password'); - } -} diff --git a/src/DevHive.Angular/src/app/components/not-found/not-found.component.css b/src/DevHive.Angular/src/app/components/not-found/not-found.component.css deleted file mode 100644 index ef6231d..0000000 --- a/src/DevHive.Angular/src/app/components/not-found/not-found.component.css +++ /dev/null @@ -1,23 +0,0 @@ -#content { - font-size: 1.3em; -} - -#content hr { - width: 100%; - border: 1px solid black; - box-sizing: border-box; -} - -#back-btns { - width: 100%; - display: flex; -} - -#back-btns > * { - flex: 1; - margin-right: .2em; -} - -#back-btns > *:nth-last-child(1) { - margin-right: 0; -} diff --git a/src/DevHive.Angular/src/app/components/not-found/not-found.component.html b/src/DevHive.Angular/src/app/components/not-found/not-found.component.html deleted file mode 100644 index 8394810..0000000 --- a/src/DevHive.Angular/src/app/components/not-found/not-found.component.html +++ /dev/null @@ -1,10 +0,0 @@ -<div id="content"> - <div class="title"> - Page not found! - </div> - <hr> - <div id="back-btns"> - <button class="submit-btn" type="submit" (click)="backToFeed()">Back to feed</button> - <button class="submit-btn" type="submit" (click)="backToLogin()">Back to login</button> - </div> -</div> diff --git a/src/DevHive.Angular/src/app/components/not-found/not-found.component.ts b/src/DevHive.Angular/src/app/components/not-found/not-found.component.ts deleted file mode 100644 index b1f8cc1..0000000 --- a/src/DevHive.Angular/src/app/components/not-found/not-found.component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Title } from '@angular/platform-browser'; -import { Router } from '@angular/router'; - -@Component({ - selector: 'app-not-found', - templateUrl: './not-found.component.html', - styleUrls: ['./not-found.component.css'] -}) -export class NotFoundComponent implements OnInit { - private _title = 'Not Found!'; - - constructor(private _titleService: Title, private _router: Router) { - this._titleService.setTitle(this._title); - } - - ngOnInit(): void { - } - - backToFeed(): void { - this._router.navigate(['/']); - } - - backToLogin(): void { - this._router.navigate(['/login']); - } -} diff --git a/src/DevHive.Angular/src/app/components/post-attachment/post-attachment.component.css b/src/DevHive.Angular/src/app/components/post-attachment/post-attachment.component.css deleted file mode 100644 index 572cc99..0000000 --- a/src/DevHive.Angular/src/app/components/post-attachment/post-attachment.component.css +++ /dev/null @@ -1,75 +0,0 @@ -/* Attachment */ - -.attachment { - border: 2px solid black; - border-top: 0; - border-radius: 0 0 .6em .6em; - padding: .4em; - padding-top: 1em; - margin-top: calc(-0.8em - 2px); -} - -.clickable { - width: 100%; - height: 100%; - display: flex; -} - -.clickable:hover { - cursor: pointer; -} - -.attachment-name { - flex: 1; -} - -.attachment-type { - margin-left: .2em; -} - -/* Full attachment */ - -.show-full-attachment { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - background-color: #000000AF; - display: flex; - justify-content: center; - align-items: center; - z-index: 2; -} - -.show-full-attachment > * { - max-height: 100vh; - max-width: 100vw; -} - -.attachment-download { - max-width: 420px !important; - margin: 0 .4em; - text-decoration: none; - color: white; - border-color: white; - background-color: black; -} - -.attachment-download:hover { - background-color: white; - color: var(--focus-color); -} - -.close { - position: fixed; - top: .2em; - right: .2em; - font-size: 2em; - color: whitesmoke; -} - -.close:hover { - color: var(--failure); - cursor: pointer; -} diff --git a/src/DevHive.Angular/src/app/components/post-attachment/post-attachment.component.html b/src/DevHive.Angular/src/app/components/post-attachment/post-attachment.component.html deleted file mode 100644 index 4d381d1..0000000 --- a/src/DevHive.Angular/src/app/components/post-attachment/post-attachment.component.html +++ /dev/null @@ -1,18 +0,0 @@ -<div class="attachment"> - <div class="clickable" (click)="toggleShowFull()"> - <div class="attachment-name"> - {{ fileName }} - </div> - <div class="attachment-type"> - {{ fileType }} - </div> - </div> -</div> - -<div class="show-full-attachment" *ngIf="showFull" (click)="toggleShowFull()"> - <img class="attachment-img" *ngIf="isImage" src="{{paramURL}}"> - <a class="attachment-download submit-btn" *ngIf="!isImage" href="{{paramURL}}">Download attachment</a> - <div class="close"> - ☒ - </div> -</div> diff --git a/src/DevHive.Angular/src/app/components/post-attachment/post-attachment.component.ts b/src/DevHive.Angular/src/app/components/post-attachment/post-attachment.component.ts deleted file mode 100644 index 1d00def..0000000 --- a/src/DevHive.Angular/src/app/components/post-attachment/post-attachment.component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component, Input, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-post-attachment', - templateUrl: './post-attachment.component.html', - styleUrls: ['./post-attachment.component.css'] -}) -export class PostAttachmentComponent implements OnInit { - @Input() paramURL: string; - public isImage = false; - public showFull = false; - public fileName: string; - public fileType: string; - - constructor() - { } - - ngOnInit(): void { - this.isImage = this.paramURL.includes('image') && !this.paramURL.endsWith('pdf'); - this.fileType = this.isImage ? 'img' : 'raw'; - this.fileName = this.paramURL.match('(?<=\/)(?:.(?!\/))+$')?.pop() ?? 'Attachment'; - } - - toggleShowFull(): void { - this.showFull = !this.showFull; - } -} diff --git a/src/DevHive.Angular/src/app/components/post-page/post-page.component.css b/src/DevHive.Angular/src/app/components/post-page/post-page.component.css deleted file mode 100644 index 3eec851..0000000 --- a/src/DevHive.Angular/src/app/components/post-page/post-page.component.css +++ /dev/null @@ -1,62 +0,0 @@ -#content { - justify-content: flex-start !important; -} - -#content > * { - width: 100%; -} - -.many-buttons { - width: 100%; - display: flex; -} - -.many-buttons > * { - flex: 1; - margin-right: .3em; -} - -.many-buttons > *:last-of-type { - margin-right: 0; -} - -#editPost { - display: flex; - position: relative; -} - -#new-message-input { - flex: 1; - box-sizing: border-box; -} - -#file-upload { - font-size: inherit; - color: transparent; - width: 1.99em; - height: 1.99em; - margin-left: .3em; -} - -#file-upload:hover { - cursor: pointer; -} - -#file-upload::-webkit-file-upload-button { - visibility: hidden; -} - -#attachment-img { - height: 1.99em; - width: 1.99em; - position: absolute; - right: 0; - pointer-events: none; -} - - -.submit-btn { - max-width: 98%; - margin: 0 auto; - margin-bottom: .5em; -} diff --git a/src/DevHive.Angular/src/app/components/post-page/post-page.component.html b/src/DevHive.Angular/src/app/components/post-page/post-page.component.html deleted file mode 100644 index 8665865..0000000 --- a/src/DevHive.Angular/src/app/components/post-page/post-page.component.html +++ /dev/null @@ -1,37 +0,0 @@ -<app-loading *ngIf="!dataArrived"></app-loading> - -<div id="content" *ngIf="dataArrived"> - <div class="many-buttons" *ngIf="loggedIn"> - <button class="submit-btn" type="submit" (click)="backToFeed()">ᐊ Back to feed</button> - <button class="submit-btn" type="submit" (click)="backToProfile()">ᐊ Back to profile</button> - </div> - <button class="submit-btn" type="submit" (click)="toLogin()" *ngIf="!loggedIn">Login</button> - <app-post [paramId]="postId.toString()"></app-post> - <div class="many-buttons" *ngIf="editable"> - <button class="submit-btn" (click)="editPost()">Edit post</button> - <button class="submit-btn delete-btn" (click)="deletePost()">Delete post</button> - </div> - <form id="editPost" [formGroup]="editPostFormGroup" *ngIf="editingPost" (ngSubmit)="editPost()"> - <input id="new-message-input" type="text" placeholder="New post message" class="input-field" formControlName="newPostMessage"> - <img id="attachment-img" src="assets/images/paper-clip.png"> - <input id="file-upload" type="file" formControlName="fileUpload" (change)="onFileUpload($event)" multiple> - <input type="submit" style="display: none" /> - </form> - <div class="form-attachments" *ngIf="editingPost"> - <div *ngFor="let file of files" class="form-attachment"> - {{ file.name ? file.name : 'Attachment' }} - <div class="remove-form-attachment" (click)="removeAttachment(file.name)"> - ☒ - </div> - </div> - </div> - <form [formGroup]="addCommentFormGroup" (ngSubmit)="addComment()"> - <input type="text" placeholder="Add comment" class="input-field" formControlName="newComment"> - <input type="submit" style="display: none" /> - </form> - <div> - <div class="comment" *ngFor="let comm of post?.comments"> - <app-comment [paramId]="comm.id.toString()"></app-comment> - </div> - </div> -<div> diff --git a/src/DevHive.Angular/src/app/components/post-page/post-page.component.ts b/src/DevHive.Angular/src/app/components/post-page/post-page.component.ts deleted file mode 100644 index 413ff80..0000000 --- a/src/DevHive.Angular/src/app/components/post-page/post-page.component.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { HttpErrorResponse } from '@angular/common/http'; -import { Component, OnInit } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; -import { Title } from '@angular/platform-browser'; -import { Router } from '@angular/router'; -import { Guid } from 'guid-typescript'; -import { CommentService } from 'src/app/services/comment.service'; -import { PostService } from 'src/app/services/post.service'; -import { TokenService } from 'src/app/services/token.service'; -import { Post } from 'src/models/post'; -import { CloudinaryService } from 'src/app/services/cloudinary.service'; - -@Component({ - selector: 'app-post-page', - templateUrl: './post-page.component.html', - styleUrls: ['./post-page.component.css'] -}) -export class PostPageComponent implements OnInit { - private _title = 'Post'; - public dataArrived = false; - public loggedIn = false; - public editable = false; - public editingPost = false; - public postId: Guid; - public post: Post; - public files: File[]; - public editPostFormGroup: FormGroup; - public addCommentFormGroup: FormGroup; - - constructor(private _titleService: Title, private _router: Router, private _fb: FormBuilder, private _tokenService: TokenService, private _postService: PostService, private _commentService: CommentService, private _cloudinaryService: CloudinaryService){ - this._titleService.setTitle(this._title); - } - - ngOnInit(): void { - this.loggedIn = this._tokenService.getTokenFromSessionStorage() !== ''; - this.postId = Guid.parse(this._router.url.substring(6)); - this.files = []; - - // Gets the post and the logged in user and compares them, - // to determine if the current post is made by the user - this._postService.getPostRequest(this.postId).subscribe( - (result: object) => { - this.post = result as Post; - this.post.fileURLs = Object.values(result)[7]; - if (this.loggedIn) { - this.editable = this.post.creatorUsername === this._tokenService.getUsernameFromSessionStorageToken(); - } - if (this.post.fileURLs.length > 0) { - this.loadFiles(); - } - else { - this.dataArrived = true; - } - }, - (err: HttpErrorResponse) => { - this._router.navigate(['/not-found']); - } - ); - - this.editPostFormGroup = this._fb.group({ - newPostMessage: new FormControl(''), - fileUpload: new FormControl('') - }); - - this.addCommentFormGroup = this._fb.group({ - newComment: new FormControl('') - }); - } - - private loadFiles(): void { - for (const fileURL of this.post.fileURLs) { - this._cloudinaryService.getFileRequest(fileURL).subscribe( - (result: object) => { - const file = result as File; - const tmp = { - name: fileURL.match('(?<=\/)(?:.(?!\/))+$')?.pop() ?? 'Attachment' - }; - - Object.assign(file, tmp); - this.files.push(file); - - if (this.files.length === this.post.fileURLs.length) { - this.dataArrived = true; - } - } - ); - } - } - - backToFeed(): void { - this._router.navigate(['/']); - } - - backToProfile(): void { - this._router.navigate(['/profile/' + this._tokenService.getUsernameFromSessionStorageToken()]); - } - - toLogin(): void { - this._router.navigate(['/login']); - } - - onFileUpload(event: any): void { - this.files.push(...event.target.files); - this.editPostFormGroup.get('fileUpload')?.reset(); - } - - removeAttachment(fileName: string): void { - this.files = this.files.filter(x => x.name !== fileName); - } - - editPost(): void { - if (this._tokenService.getTokenFromSessionStorage() === '') { - this.toLogin(); - return; - } - - if (this.editingPost) { - let newMessage = this.editPostFormGroup.get('newPostMessage')?.value; - if (newMessage === '') { - newMessage = this.post.message; - } - this._postService.putPostWithSessionStorageRequest(this.postId, newMessage, this.files).subscribe( - (result: object) => { - this.reloadPage(); - } - ); - this.dataArrived = false; - } - this.editingPost = !this.editingPost; - } - - addComment(): void { - if (!this.loggedIn) { - this._router.navigate(['/login']); - return; - } - - const newComment = this.addCommentFormGroup.get('newComment')?.value; - if (newComment !== '' && newComment !== null) { - this._commentService.createCommentWithSessionStorageRequest(this.postId, newComment).subscribe( - (result: object) => { - this.editPostFormGroup.reset(); - this.reloadPage(); - } - ); - } - } - - deletePost(): void { - this._postService.deletePostWithSessionStorage(this.postId).subscribe( - (result: object) => { - this._router.navigate(['/profile/' + this._tokenService.getUsernameFromSessionStorageToken()]); - } - ); - } - - private reloadPage(): void { - this._router.routeReuseStrategy.shouldReuseRoute = () => false; - this._router.onSameUrlNavigation = 'reload'; - this._router.navigate([this._router.url]); - } -} diff --git a/src/DevHive.Angular/src/app/components/post/post.component.css b/src/DevHive.Angular/src/app/components/post/post.component.css deleted file mode 100644 index 1b88c7d..0000000 --- a/src/DevHive.Angular/src/app/components/post/post.component.css +++ /dev/null @@ -1,134 +0,0 @@ -.post { - display: flex; - width: 98%; - - margin: .5em auto; - box-sizing: border-box; - padding: .5em; - background-color: var(--card-bg); - position: relative; -} - -.post:first-child { - margin-top: 0; -} - -hr { - border: 1px solid black; - width: 90%; -} - -/* Author */ - -.author { - display: flex; - margin-bottom: .2em; -} - -.author:hover { - cursor: pointer; -} - -.author > img { - width: 2.2em; - height: 2.2em; - margin-right: .2em; -} - -.author-info > .handle { - font-size: .9em; - color: gray; -} - -/* Content */ - -.content { - flex: 1; -} - -.message { - margin: .3em 0; - word-break: break-all; -} - -.bottom-post { - font-size: .5em; - color: gray; - display: flex; - align-items: center; -} - -.separator { - margin: 0 .5em; -} - -.comment-count { - font-size: 1em; -} - -.comment-count > img { - height: .8em; -} - -.message:hover, .timestamp:hover { - cursor: pointer; -} - -/* Rating */ - -/* Temporary, until ratings are implemented fully */ -.rating { - display: none !important; -} - -.rating { - display: flex; - flex-direction: column; - align-items: center; - min-height: 4.4em; - margin: auto -.1em auto 0; -} - -.score { - flex: 1; - display: flex; - align-items: center; -} - - -.vote { - display: flex; - align-items: center; - flex: 1; - - background: var(--card-bg); - font-size: 1em; - - border: 1px solid var(--card-bg); - box-sizing: border-box; - border-radius: .2em; - - } - -.vote:hover { - border: 1px solid var(--focus-color); - color: var(--focus-color); - cursor: pointer; -} - -/* Attachments */ - -.attachments { - display: flex; - width: 98%; - margin: -.3em auto .5em auto; - flex-wrap: wrap; -} - -.attachments:empty { - display: none; -} - -.attachments > * { - flex: 1; -} diff --git a/src/DevHive.Angular/src/app/components/post/post.component.html b/src/DevHive.Angular/src/app/components/post/post.component.html deleted file mode 100644 index bc0d84a..0000000 --- a/src/DevHive.Angular/src/app/components/post/post.component.html +++ /dev/null @@ -1,49 +0,0 @@ -<app-loading *ngIf="!loaded"></app-loading> - -<div class="post rounded-border" *ngIf="loaded"> - <div class="content"> - <div class="author" (click)="goToAuthorProfile()"> - <img class="round-image" [src]="user.profilePictureURL"> - <div class="author-info"> - <div class="name"> - {{ user.firstName }} {{ user.lastName }} - </div> - <div class="handle"> - @{{ user.userName }} - </div> - </div> - </div> - <div class="message" (click)="goToPostPage()"> - {{ post.message }} - </div> - <div class="bottom-post" (click)="goToPostPage()"> - <div class="timestamp"> - {{ timeCreated }} - </div> - <div class="separator"> - ║ - </div> - <div class="comment-count"> - {{ post.comments.length }} - <img src="assets/images/comment.png"> - </div> - </div> - - </div> - <div class="rating"> - <button class="vote"> - ᐃ - </button> - <div class="score"> - {{ votesNumber }} - </div> - <button class="vote"> - ᐁ - </button> - </div> -</div> -<div class="attachments"> - <div *ngFor="let fileURL of post.fileURLs"> - <app-post-attachment class="no-events" [paramURL]="fileURL"></app-post-attachment> - </div> -</div> diff --git a/src/DevHive.Angular/src/app/components/post/post.component.ts b/src/DevHive.Angular/src/app/components/post/post.component.ts deleted file mode 100644 index 387f56f..0000000 --- a/src/DevHive.Angular/src/app/components/post/post.component.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { Guid } from 'guid-typescript'; -import { PostService } from 'src/app/services/post.service'; -import { UserService } from 'src/app/services/user.service'; -import { User } from 'src/models/identity/user'; -import { Post } from 'src/models/post'; - -@Component({ - selector: 'app-post', - templateUrl: './post.component.html', - styleUrls: ['./post.component.css'], -}) -export class PostComponent implements OnInit { - public loaded = false; - public user: User; - public post: Post; - public votesNumber: number; - public timeCreated: string; - @Input() paramId: string; - - constructor(private _postService: PostService, private _userService: UserService, private _router: Router) - { } - - ngOnInit(): void { - this.post = this._postService.getDefaultPost(); - this.user = this._userService.getDefaultUser(); - - this._postService.getPostRequest(Guid.parse(this.paramId)).subscribe( - (result: object) => { - Object.assign(this.post, result); - this.post.fileURLs = Object.values(result)[7]; - this.votesNumber = 23; - - this.timeCreated = new Date(this.post.timeCreated).toLocaleString('en-GB'); - this.loadUser(); - } - ); - } - - private loadUser(): void { - this._userService.getUserByUsernameRequest(this.post.creatorUsername).subscribe( - (result: object) => { - Object.assign(this.user, result); - this.loaded = true; - } - ); - } - - goToAuthorProfile(): void { - this._router.navigate(['/profile/' + this.user.userName]); - } - - goToPostPage(): void { - this._router.navigate(['/post/' + this.post.postId]); - } -} diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css deleted file mode 100644 index 1c07d9f..0000000 --- a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.css +++ /dev/null @@ -1,124 +0,0 @@ -* { - box-sizing: border-box; -} - -#content { - max-width: 22em; - justify-content: start; -} - -form { - width: 100%; -} - -hr { - width: calc(100% - 1em); - color: black; - border: 1px solid black; -} - -/* Navigation bar (for loggedin user) */ - -#navigation { - display: flex; - width: 100%; -} - -#navigation > .submit-btn { - flex: 1; - margin-top: 0; - margin-left: .5em; - font-size: inherit; -} - -#navigation > .submit-btn:nth-of-type(1) { - margin-left: 0; -} - -/* Form */ - -#update-profile-picture { - display: flex; - align-items: center; - justify-content: center; - padding: 0 .5em; - flex-wrap: wrap; -} - -#profile-picture { - width: 5em; - height: 5em; -} - -#submit-file { - display: flex; - flex-direction: column; - align-items: center; - padding: 1em; -} - -#upload-file:hover { - cursor: inherit; -} - -#upload-file > input:hover { - cursor: pointer; -} - -#upload-file > input::-webkit-file-upload-button { - visibility: hidden; -} - -#update-user { - margin-top: 1.1em; -} - -.input-field { - border-color: var(--focus-color) !important; - caret-color: var(--focus-color); - border-width: 2px !important; - margin-top: -1px !important; -} - -.input-field-label { - font-size: .7em; - color: var(--focus-color); - transform: translate(0, -1.2em); -} - -#all-languages, #all-technologies { - display: flex; - flex-wrap: wrap; -} - -/* Buttons */ - -.edit-btn { - border-radius: 0 !important; - color: var(--focus-color); - background-color: white; - border-color: var(--focus-color); -} - -.edit-btn:hover { - color: white; - background-color: black; - border-color: black !important; -} - -.submit-btn { - margin-bottom: .5em; -} - -#update-profile-btn { - margin-top: 1em; -} - -#confirm-delete { - box-sizing: border-box; - width: 100%; - background-color: var(--failure); - color: white; - padding: .2em; - text-align: center; -} diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html deleted file mode 100644 index 502697d..0000000 --- a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.html +++ /dev/null @@ -1,116 +0,0 @@ -<app-loading *ngIf="!dataArrived"></app-loading> - -<div id="content" *ngIf="dataArrived"> - <nav id="navigation"> - <button class="submit-btn" (click)="goToProfile()">ᐊ Back</button> - <button class="submit-btn" (click)="navigateToAdminPanel()" *ngIf="isAdminUser">Panel</button> - <button class="submit-btn" (click)="logout()">Logout</button> - </nav> - <hr> - <div class="scroll-standalone"> - <form id="update-profile-picture" [formGroup]="updateProfilePictureFormGroup" (ngSubmit)="updateProfilePicture()"> - <img id="profile-picture" class="round-image" [src]="user.profilePictureURL"> - <div id="submit-file"> - <div id="upload-file" class="submit-btn"> - <input type="file" accept="image/*" formControlName="fileUpload" (change)="onFileUpload($event)"> - </div> - <button class="submit-btn" type="submit">Update profile picture</button> - </div> - </form> - <hr> - <form id="update-user" [formGroup]="updateUserFormGroup" (ngSubmit)="onSubmit()"> - <div class="input-selection"> - <input type="text" class="input-field" formControlName="firstName" required> - <label class="input-field-label">First Name</label> - - <div class="input-errors"> - <label *ngIf="updateUserFormGroup.get('firstName')?.errors?.required" class="error">*Required</label> - <label *ngIf="updateUserFormGroup.get('firstName')?.errors?.minlength" class="error">*Minimum 3 characters</label> - </div> - </div> - - <div class="input-selection"> - <input type="text" class="input-field" formControlName="lastName" required> - <label class="input-field-label">Last Name</label> - - <div class="input-errors"> - <label *ngIf="updateUserFormGroup.get('lastName')?.errors?.required" class="error">*Required</label> - <label *ngIf="updateUserFormGroup.get('lastName')?.errors?.minlength" class="error">*Minimum 3 characters</label> - </div> - </div> - - <div class="input-selection"> - <input type="text" class="input-field" formControlName="username" required> - <label class="input-field-label">Username</label> - - <div class="input-errors"> - <label *ngIf="updateUserFormGroup.get('username')?.errors?.required" class="error">*Required</label> - <label *ngIf="updateUserFormGroup.get('username')?.errors?.minlength" class="error">*Minimum 3 characters</label> - </div> - </div> - - <div class="input-selection"> - <input type="text" class="input-field" formControlName="email" required> - <label class="input-field-label">Email</label> - - <div class="input-errors"> - <label *ngIf="updateUserFormGroup.get('email')?.errors?.required" class="error">*Required</label> - <label *ngIf="updateUserFormGroup.get('email')?.errors?.email" class="error">*Invalid email</label> - </div> - </div> - - <div class="input-selection"> - <input type="password" class="input-field" formControlName="password" required> - <label class="input-field-label">Password</label> - - <div class="input-errors"> - <label *ngIf="updateUserFormGroup.get('password')?.errors?.required" class="error">*Required</label> - <label *ngIf="updateUserFormGroup.get('password')?.errors?.minlength" class="error">*Minimum 3 characters</label> - <label *ngIf="updateUserFormGroup.get('password')?.errors?.pattern" class="error">*At least 1 number</label> - </div> - </div> - <button type="button" class="submit-btn edit-btn" (click)="toggleLanguages()">▼ Edit Languages ▼</button> - <div *ngIf="showLanguages"> - <div class="input-selection"> - <input type="text" class="input-field" formControlName="languageInput" required> - - <div class="input-errors"> - <label class="error">Type in your desired languages, separated by a space</label> - </div> - </div> - Available languages: - <div id="all-languages"> - <div class="user-language" *ngFor="let lang of availableLanguages"> - {{ lang.name }} - </div> - </div> - </div> - - <button type="button" class="submit-btn edit-btn" (click)="toggleTechnologies()">▼ Edit Technologies ▼</button> - <div *ngIf="showTechnologies"> - <div class="input-selection"> - <input type="text" class="input-field" formControlName="technologyInput" required> - - <div class="input-errors"> - <label class="error">Type in your desired technologies, separated by a space</label> - </div> - </div> - Available technologies: - <div id="all-technologies"> - <div class="user-technology" *ngFor="let tech of availableTechnologies"> - {{ tech.name }} - </div> - </div> - </div> - - <button id="update-profile-btn" class="submit-btn" type="submit">Update profile</button> - <app-success-bar></app-success-bar> - <app-error-bar></app-error-bar> - </form> - <hr> - <div id="confirm-delete" *ngIf="deleteAccountConfirm"> - Are you sure you want to delete your account?<br>This is permanent! - </div> - <button class="submit-btn delete-btn" (click)="deleteAccount()">Delete account</button> - </div> -</div> diff --git a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts b/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts deleted file mode 100644 index a484665..0000000 --- a/src/DevHive.Angular/src/app/components/profile-settings/profile-settings.component.ts +++ /dev/null @@ -1,307 +0,0 @@ -import { Location } from '@angular/common'; -import { HttpErrorResponse } from '@angular/common/http'; -import { Component, OnInit, ViewChild } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; -import { Router } from '@angular/router'; -import { LanguageService } from 'src/app/services/language.service'; -import { UserService } from 'src/app/services/user.service'; -import { TechnologyService } from 'src/app/services/technology.service'; -import { User } from 'src/models/identity/user'; -import { ErrorBarComponent } from '../error-bar/error-bar.component'; -import { SuccessBarComponent } from '../success-bar/success-bar.component'; -import { Language } from 'src/models/language'; -import { Technology } from 'src/models/technology'; -import { TokenService } from 'src/app/services/token.service'; -import { Title } from '@angular/platform-browser'; -import { AppConstants } from 'src/app/app-constants.module'; - -@Component({ - selector: 'app-profile-settings', - templateUrl: './profile-settings.component.html', - styleUrls: ['./profile-settings.component.css'] -}) -export class ProfileSettingsComponent implements OnInit { - private _title = 'Profile Settings'; - @ViewChild(ErrorBarComponent) private _errorBar: ErrorBarComponent; - @ViewChild(SuccessBarComponent) private _successBar: SuccessBarComponent; - private _urlUsername: string; - public isAdminUser = false; - public dataArrived = false; - public deleteAccountConfirm = false; - public showLanguages = false; - public showTechnologies = false; - public updateUserFormGroup: FormGroup; - public updateProfilePictureFormGroup: FormGroup; - public newProfilePicture: File; - public user: User; - public availableLanguages: Language[]; - public availableTechnologies: Technology[]; - - constructor(private _titleService: Title, private _router: Router, private _userService: UserService, private _languageService: LanguageService, private _technologyService: TechnologyService, private _tokenService: TokenService, private _fb: FormBuilder, private _location: Location) { - this._titleService.setTitle(this._title); - } - - ngOnInit(): void { - this._urlUsername = this._router.url.substring(9); - this._urlUsername = this._urlUsername.substring(0, this._urlUsername.length - 9); - - this.user = this._userService.getDefaultUser(); - this.availableLanguages = []; - this.availableTechnologies = []; - this.newProfilePicture = new File([], ''); - - this._userService.getUserByUsernameRequest(this._urlUsername).subscribe( - (res: object) => { - Object.assign(this.user, res); - this.isAdminUser = this.user.roles.map(x => x.name).includes(AppConstants.ADMIN_ROLE_NAME); - this.finishUserLoading(); - }, - (err: HttpErrorResponse) => { - this._router.navigate(['/not-found']); - } - ); - - this._languageService.getAllLanguagesWithSessionStorageRequest().subscribe( - (result: object) => { - this.availableLanguages = result as Language[]; - } - ); - this._technologyService.getAllTechnologiesWithSessionStorageRequest().subscribe( - (result: object) => { - this.availableTechnologies = result as Technology[]; - } - ); - } - - private finishUserLoading(): void { - if (sessionStorage.getItem('UserCred')) { - const userFromToken: User = this._userService.getDefaultUser(); - - this._userService.getUserFromSessionStorageRequest().subscribe( - (tokenRes: object) => { - Object.assign(userFromToken, tokenRes); - - if (userFromToken.userName === this._urlUsername) { - this.initForms(); - this.dataArrived = true; - } - else { - this.goToProfile(); - } - }, - (err: HttpErrorResponse) => { - this.logout(); - } - ); - } - else { - this.goToProfile(); - } - } - - private initForms(): void { - this.updateUserFormGroup = this._fb.group({ - firstName: new FormControl(this.user.firstName, [ - Validators.required, - Validators.minLength(3) - ]), - lastName: new FormControl(this.user.lastName, [ - Validators.required, - Validators.minLength(3) - ]), - username: new FormControl(this.user.userName, [ - Validators.required, - Validators.minLength(3) - ]), - email: new FormControl(this.user.email, [ - Validators.required, - Validators.email, - ]), - password: new FormControl('', [ - Validators.required, - Validators.minLength(3), - Validators.pattern('.*[0-9].*') // Check if password contains atleast one number - ]), - - // For language we have two different controls, - // the first one is used for input, the other one for sending data - // because if we edit the control for input, - // we're also gonna change the input field in the HTML - languageInput: new FormControl(''), // The one for input - languages: new FormControl(''), // The one that is sent - - // For technologies it's the same as it is with languages - technologyInput: new FormControl(''), - technologies: new FormControl('') - }); - - this.getLanguagesForShowing().then(value => { - this.updateUserFormGroup.patchValue({ languageInput : value }); - }); - - this.getTechnologiesForShowing().then(value => { - this.updateUserFormGroup.patchValue({ technologyInput : value }); - }); - - this.updateProfilePictureFormGroup = this._fb.group({ - fileUpload: new FormControl('') - }); - - this.updateUserFormGroup.valueChanges.subscribe(() => { - this._successBar?.hideMsg(); - this._errorBar?.hideError(); - }); - } - - private getLanguagesForShowing(): Promise<string> { - return new Promise(resolve => { - this._languageService.getFullLanguagesFromIncomplete(this.user.languages).then(value => { - this.user.languages = value; - resolve(value.map(x => x.name).join(' ')); - }); - }); - } - - private getTechnologiesForShowing(): Promise<string> { - return new Promise(resolve => { - this._technologyService.getFullTechnologiesFromIncomplete(this.user.technologies).then(value => { - this.user.technologies = value; - resolve(value.map(x => x.name).join(' ')); - }); - }); - } - - onFileUpload(event: any): void { - this.newProfilePicture = event.target.files[0]; - } - - updateProfilePicture(): void { - if (this.newProfilePicture.size === 0) { - return; - } - - this._userService.putProfilePictureFromSessionStorageRequest(this.newProfilePicture).subscribe( - (result: object) => { - this.reloadPage(); - } - ); - this.dataArrived = false; - } - - onSubmit(): void { - this._successBar.hideMsg(); - this._errorBar.hideError(); - - this.patchLanguagesControl(); - this.patchTechnologiesControl(); - - this._userService.putUserFromSessionStorageRequest(this.updateUserFormGroup, this.user.roles, this.user.friends).subscribe( - (result: object) => { - this._successBar.showMsg('Profile updated successfully!'); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - - private patchLanguagesControl(): void { - // Get user input - const langControl = this.updateUserFormGroup.get('languageInput')?.value as string ?? ''; - - if (langControl === '') { - // Add the data to the form (to the value that is going to be sent) - this.updateUserFormGroup.patchValue({ - languages : [] - }); - } - else { - const names = langControl.split(' '); - - // Transfer user input to objects of type { "name": "value" } - const actualLanguages = []; - for (const lName of names) { - if (lName !== '') { - actualLanguages.push({ name : lName }); - } - } - - // Add the data to the form (to the value that is going to be sent) - this.updateUserFormGroup.patchValue({ - languages : actualLanguages - }); - } - } - - private patchTechnologiesControl(): void { - // Get user input - const techControl = this.updateUserFormGroup.get('technologyInput')?.value as string ?? ''; - - if (techControl === '') { - // Add the data to the form (to the value that is going to be sent) - this.updateUserFormGroup.patchValue({ - technologies : [] - }); - } - else { - const names = techControl.split(' '); - - // Transfer user input to objects of type { "name": "value" } - const actualTechnologies = []; - for (const tName of names) { - if (tName !== '') { - actualTechnologies.push({ name : tName }); - } - } - - // Add the data to the form (to the value that is going to be sent) - this.updateUserFormGroup.patchValue({ - technologies : actualTechnologies - }); - } - } - - goToProfile(): void { - this._router.navigate([this._router.url.substring(0, this._router.url.length - 9)]); - } - - navigateToAdminPanel(): void { - this._router.navigate(['/admin-panel']); - } - - logout(): void { - this._tokenService.logoutUserFromSessionStorage(); - this.goToProfile(); - } - - toggleLanguages(): void { - this.showLanguages = !this.showLanguages; - } - - toggleTechnologies(): void { - this.showTechnologies = !this.showTechnologies; - } - - deleteAccount(): void { - if (this.deleteAccountConfirm) { - this._userService.deleteUserFromSessionStorageRequest().subscribe( - (res: object) => { - this.logout(); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - this.dataArrived = false; - } - else { - this.deleteAccountConfirm = true; - } - } - - private reloadPage(): void { - this._router.routeReuseStrategy.shouldReuseRoute = () => false; - this._router.onSameUrlNavigation = 'reload'; - this._router.navigate([this._router.url]); - } -} diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.css b/src/DevHive.Angular/src/app/components/profile/profile.component.css deleted file mode 100644 index ebcd406..0000000 --- a/src/DevHive.Angular/src/app/components/profile/profile.component.css +++ /dev/null @@ -1,105 +0,0 @@ -* { - box-sizing: border-box; -} - -#content { - max-width: 22em; - justify-content: start; -} - -hr { - width: calc(100% - 1em); - color: black; - border: 1px solid black; -} - -form { - width: 100%; -} - -/* Navigation bar (for loggedin user) */ - -#navigation { - display: flex; - width: 100%; -} - -.submit-btn { - flex: 1; - margin-top: 0; - margin-left: .5em; - font-size: inherit; -} - -#navigation > .submit-btn:first-child { - margin-left: 0; -} - -/* Top card */ - -#main-info { - display: flex; - width: 100%; - margin-bottom: .25em -} - -#main-info > img { - width: 5em; - height: 5em; -} - -#other-main-info { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - text-align: center; -} - -#other-main-info > * { - font-size: 1.4em; -} - -#other-main-info *:nth-child(1) { - margin-top: auto; -} - -#other-main-info *:nth-last-child(1) { - margin-bottom: auto; -} - -#add-friend, #loggedin-password { - flex: 0 !important; - margin-top: .4em; - max-width: 8em; - font-size: .6em !important; -} - -#loggedin-password { - max-width: 100%; -} - -/* Languages and technologies */ - -.secondary-info { - margin-top: .25em; - margin-bottom: .25em; - width: 100%; - display: flex; - align-items: center; - flex-wrap: wrap; -} - -/* Posts */ - -#no-posts { - width: 100%; - text-align: center; - color: gray; - margin-top: .2em; -} - -#posts { - width: 100%; - height: 100%; -} diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.html b/src/DevHive.Angular/src/app/components/profile/profile.component.html deleted file mode 100644 index 0e5f633..0000000 --- a/src/DevHive.Angular/src/app/components/profile/profile.component.html +++ /dev/null @@ -1,60 +0,0 @@ -<app-loading *ngIf="!dataArrived"></app-loading> - -<div id="content" *ngIf="dataArrived"> - <nav id="navigation"> - <button class="submit-btn" (click)="goBack()">ᐊ Back</button> - <button class="submit-btn" (click)="navigateToSettings()" *ngIf="isTheLoggedInUser">Settings</button> - <button class="submit-btn" (click)="navigateToAdminPanel()" *ngIf="isTheLoggedInUser && isAdminUser">Panel</button> - <button class="submit-btn" (click)="logout()" *ngIf="isTheLoggedInUser">Logout</button> - </nav> - <hr> - <div class="scroll-standalone" (scroll)="onScroll($event)"> - <div id="main-info" class="rounded-border"> - <img class="round-image" [src]="user.profilePictureURL" alt=""/> - <div id="other-main-info"> - <div id="name"> - {{ user.firstName }} {{ user.lastName }} - </div> - <div id="username"> - @{{ user.userName }} - </div> - <form [formGroup]="updateFrienship" (ngSubmit)="modifyFriend()" *ngIf="!isTheLoggedInUser && isUserLoggedIn"> - <button id="add-friend" type="submit" class="submit-btn">{{ friendOfUser ? 'Unfriend' : 'Add friend' }}</button> - <br> - <input id="loggedin-password" type="password" formControlName="password" class="input-field" *ngIf="updatingFriendship" placeholder="Type in password to confirm"> - </form> - </div> - </div> - <div class="secondary-info rounded-border"> - Languages: - <div *ngFor="let lang of user.languages"> - <div class="user-language"> - {{ lang.name }} - </div> - </div> - <div *ngIf="user.languages.length === 0"> - None - </div> - </div> - <div class="secondary-info rounded-border"> - Technologies: - <div *ngFor="let tech of user.technologies"> - <div class="user-language"> - {{ tech.name }} - </div> - </div> - <div *ngIf="user.technologies.length === 0"> - None - </div> - </div> - <hr> - <div id="posts"> - <div id="no-posts" *ngIf="userPosts.length === 0"> - {{ user.firstName }} {{ user.lastName }} hasn't posted anything yet! - </div> - <div *ngFor="let userPost of userPosts"> - <app-post [paramId]="userPost.postId.toString()"></app-post> - </div> - </div> - </div> -</div> diff --git a/src/DevHive.Angular/src/app/components/profile/profile.component.ts b/src/DevHive.Angular/src/app/components/profile/profile.component.ts deleted file mode 100644 index bbf8585..0000000 --- a/src/DevHive.Angular/src/app/components/profile/profile.component.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { UserService } from 'src/app/services/user.service'; -import { User } from 'src/models/identity/user'; -import { AppConstants } from 'src/app/app-constants.module'; -import { HttpErrorResponse } from '@angular/common/http'; -import { Location } from '@angular/common'; -import { LanguageService } from 'src/app/services/language.service'; -import { TechnologyService } from 'src/app/services/technology.service'; -import { Post } from 'src/models/post'; -import { FeedService } from 'src/app/services/feed.service'; -import { TokenService } from 'src/app/services/token.service'; -import { Title } from '@angular/platform-browser'; -import { Friend } from 'src/models/identity/friend'; -import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; - -@Component({ - selector: 'app-profile', - templateUrl: './profile.component.html', - styleUrls: ['./profile.component.css'] -}) -export class ProfileComponent implements OnInit { - private _title = 'Profile'; - private _urlUsername: string; - private _timeLoaded: string; - private _currentPage: number; - public isTheLoggedInUser = false; - public isUserLoggedIn = false; - public isAdminUser = false; - public dataArrived = false; - public friendOfUser = false; - public updatingFriendship = false; - public user: User; - public userPosts: Post[]; - public updateFrienship: FormGroup; - - constructor(private _titleService: Title, private _fb: FormBuilder, private _router: Router, private _userService: UserService, private _languageService: LanguageService, private _technologyService: TechnologyService, private _feedService: FeedService, private _location: Location, private _tokenService: TokenService) { - this._titleService.setTitle(this._title); - } - - private setDefaultUser(): void { - this.user = this._userService.getDefaultUser(); - } - - ngOnInit(): void { - this._urlUsername = this._router.url.substring(9); - - const now = new Date(); - now.setHours(now.getHours() + 2); // accounting for eastern europe timezone - this._timeLoaded = now.toISOString(); - this._currentPage = 1; - - this.user = this._userService.getDefaultUser(); - this.userPosts = []; - - this.updateFrienship = this._fb.group({ - password: new FormControl('') - }); - - this._userService.getUserByUsernameRequest(this._urlUsername).subscribe( - (res: object) => { - Object.assign(this.user, res); - this.isAdminUser = this.user.roles.map(x => x.name).includes(AppConstants.ADMIN_ROLE_NAME); - this.loadLanguages(); - }, - (err: HttpErrorResponse) => { - this._router.navigate(['/not-found']); - } - ); - } - - private loadLanguages(): void { - if (this.user.languages.length > 0) { - // When user has languages, get their names and load technologies - this._languageService.getFullLanguagesFromIncomplete(this.user.languages).then(value => { - this.user.languages = value; - this.loadTechnologies(); - }); - } - else { - this.loadTechnologies(); - } - } - - private loadTechnologies(): void { - if (this.user.technologies.length > 0) { - // When user has technologies, get their names and then load posts - this._technologyService.getFullTechnologiesFromIncomplete(this.user.technologies).then(value => { - this.user.technologies = value; - this.loadPosts(); - }); - } - else { - this.loadPosts(); - } - } - - private loadPosts(): void { - this._feedService.getUserPostsRequest(this.user.userName, this._currentPage++, this._timeLoaded, AppConstants.PAGE_SIZE).subscribe( - (result: object) => { - const resultArr: Post[] = Object.values(result)[0]; - this.userPosts.push(...resultArr); - this.finishUserLoading(); - }, - (err: HttpErrorResponse) => { - this._currentPage = -1; - this.finishUserLoading(); - } - ); - } - - private finishUserLoading(): void { - if (sessionStorage.getItem('UserCred')) { - this.isUserLoggedIn = true; - const userFromToken: User = this._userService.getDefaultUser(); - - this._userService.getUserFromSessionStorageRequest().subscribe( - (tokenRes: object) => { - Object.assign(userFromToken, tokenRes); - - if (userFromToken.friends.map(x => x.userName).includes(this._urlUsername)) { - this.friendOfUser = true; - } - if (userFromToken.userName === this._urlUsername) { - this.isTheLoggedInUser = true; - } - this.dataArrived = true; - }, - (err: HttpErrorResponse) => { - this.logout(); - } - ); - } - else { - this.dataArrived = true; - } - } - - goBack(): void { - this._router.navigate(['/']); - } - - navigateToAdminPanel(): void { - this._router.navigate(['/admin-panel']); - } - - navigateToSettings(): void { - this._router.navigate([this._router.url + '/settings']); - } - - logout(): void { - this._tokenService.logoutUserFromSessionStorage(); - - // Reload the page - this._router.routeReuseStrategy.shouldReuseRoute = () => false; - this._router.onSameUrlNavigation = 'reload'; - this._router.navigate([this._router.url]); - } - - modifyFriend(): void { - if (this.updatingFriendship) { - this.dataArrived = false; - - this._userService.getUserFromSessionStorageRequest().subscribe( - (result: object) => { - const loggedInUser: User = result as User; - - if (this.friendOfUser) { - loggedInUser.friends = loggedInUser.friends.filter(x => x.userName !== this.user.userName); - } - else { - const newFriend = new Friend(); - newFriend.userName = this.user.userName; - loggedInUser.friends.push(newFriend); - } - - this._userService.putBareUserFromSessionStorageRequest(loggedInUser, this.updateFrienship.get('password')?.value).subscribe( - (resultUpdate: object) => { - this.reloadPage(); - }, - (err: HttpErrorResponse) => { - this._router.navigate(['/']); - } - ); - } - ); - } - this.updatingFriendship = !this.updatingFriendship; - } - - onScroll(event: any): void { - // Detects when the element has reached the bottom, thx https://stackoverflow.com/a/50038429/12036073 - if (event.target.offsetHeight + event.target.scrollTop >= event.target.scrollHeight && this._currentPage > 0) { - this.loadPosts(); - } - } - - private reloadPage(): void { - this._router.routeReuseStrategy.shouldReuseRoute = () => false; - this._router.onSameUrlNavigation = 'reload'; - this._router.navigate([this._router.url]); - } -} diff --git a/src/DevHive.Angular/src/app/components/register/register.component.css b/src/DevHive.Angular/src/app/components/register/register.component.css deleted file mode 100644 index 93d8006..0000000 --- a/src/DevHive.Angular/src/app/components/register/register.component.css +++ /dev/null @@ -1,40 +0,0 @@ -/* A lot of stuff are moved to the global styles! */ - -* { - transition: 0.2s; -} - -form { - width: 100%; -} - -@media screen and (max-height: 630px) { - #content { - height: fit-content !important; - } -} - -#content hr { - width: 100%; - border: 1px solid black; - box-sizing: border-box; -} - -.input-selection:nth-of-type(1) { - margin-top: 1.2em; -} - -.submit-btn { - margin-bottom: .2em; -} - -.redirect-to-login { - color: var(--focus-color); - background-color: var(--bg-color); - border-color: var(--focus-color); -} - -.redirect-to-login:hover { - border-color: black !important; - color: black; -} diff --git a/src/DevHive.Angular/src/app/components/register/register.component.html b/src/DevHive.Angular/src/app/components/register/register.component.html deleted file mode 100644 index 4e67e0e..0000000 --- a/src/DevHive.Angular/src/app/components/register/register.component.html +++ /dev/null @@ -1,65 +0,0 @@ -<div id="content"> - <div class="title">Register</div> - - <form [formGroup]="registerUserFormGroup" (ngSubmit)="onSubmit()"> - <hr> - <!-- Value: {{ registerUserFormGroup.value | json }} - <hr> --> - - <div class="input-selection"> - <input type="text" placeholder="Goshko, is that u?" class="input-field" formControlName="firstName" required> - <label class="input-field-label">First Name</label> - - <div class="input-errors"> - <label *ngIf="registerUserFormGroup.get('firstName')?.errors?.required" class="error">*Required</label> - <label *ngIf="registerUserFormGroup.get('firstName')?.errors?.minlength" class="error">*Minimum 3 characters</label> - </div> - </div> - - <div class="input-selection"> - <input type="text" placeholder="Trapov? Really??" class="input-field" formControlName="lastName" required> - <label class="input-field-label">Last Name</label> - - <div class="input-errors"> - <label *ngIf="registerUserFormGroup.get('lastName')?.errors?.required" class="error">*Required</label> - <label *ngIf="registerUserFormGroup.get('lastName')?.errors?.minlength" class="error">*Minimum 3 characters</label> - </div> - </div> - - <div class="input-selection"> - <input type="text" placeholder="Think of something cool to flex on other kids" class="input-field" formControlName="username" required> - <label class="input-field-label">Username</label> - - <div class="input-errors"> - <label *ngIf="registerUserFormGroup.get('username')?.errors?.required" class="error">*Required</label> - <label *ngIf="registerUserFormGroup.get('username')?.errors?.minlength" class="error">*Minimum 3 characters</label> - </div> - </div> - - <div class="input-selection"> - <input type="text" placeholder="You expect an email joke? I have none, mail me one" class="input-field" formControlName="email" required> - <label class="input-field-label">Email</label> - - <div class="input-errors"> - <label *ngIf="registerUserFormGroup.get('email')?.errors?.required" class="error">*Required</label> - <label *ngIf="registerUserFormGroup.get('email')?.errors?.email" class="error">*Invalid email</label> - </div> - </div> - - <div class="input-selection"> - <input type="password" placeholder="Make sure it's long & strong (just like my d***)" class="input-field" formControlName="password" required> - <label class="input-field-label">Password</label> - - <div class="input-errors"> - <label *ngIf="registerUserFormGroup.get('password')?.errors?.required" class="error">*Required</label> - <label *ngIf="registerUserFormGroup.get('password')?.errors?.minlength" class="error">*Minimum 3 characters</label> - <label *ngIf="registerUserFormGroup.get('password')?.errors?.pattern" class="error">*At least 1 number</label> - </div> - </div> - - <hr> - <button class="submit-btn" type="submit">Submit</button> - <app-error-bar></app-error-bar> - </form> - <button class="submit-btn redirect-to-login" (click)="onRedirectLogin()">Already have an account? Login here</button> -</div> diff --git a/src/DevHive.Angular/src/app/components/register/register.component.ts b/src/DevHive.Angular/src/app/components/register/register.component.ts deleted file mode 100644 index 36eaa55..0000000 --- a/src/DevHive.Angular/src/app/components/register/register.component.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { HttpErrorResponse } from '@angular/common/http'; -import { Component, OnInit, ViewChild } from '@angular/core'; -import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; -import { Title } from '@angular/platform-browser'; -import { Router } from '@angular/router'; -import { TokenService } from 'src/app/services/token.service'; -import { UserService } from 'src/app/services/user.service'; -import { ErrorBarComponent } from '../error-bar/error-bar.component'; - -@Component({ - selector: 'app-register', - templateUrl: './register.component.html', - styleUrls: ['./register.component.css'] -}) -export class RegisterComponent implements OnInit { - @ViewChild(ErrorBarComponent) private _errorBar: ErrorBarComponent; - private _title = 'Register'; - public registerUserFormGroup: FormGroup; - - constructor(private _titleService: Title, private _fb: FormBuilder, private _router: Router, private _userService: UserService, private _tokenService: TokenService) { - this._titleService.setTitle(this._title); - } - - ngOnInit(): void { - this.registerUserFormGroup = this._fb.group({ - firstName: new FormControl('', [ - Validators.required, - Validators.minLength(3) - ]), - lastName: new FormControl('', [ - Validators.required, - Validators.minLength(3) - ]), - username: new FormControl('', [ - Validators.required, - Validators.minLength(3) - ]), - email: new FormControl('', [ - Validators.required, - Validators.email, - ]), - password: new FormControl('', [ - Validators.required, - Validators.minLength(3), - Validators.pattern('.*[0-9].*') // Check if password contains atleast one number - ]), - }); - - // this.registerUserFormGroup.valueChanges.subscribe(console.log); - } - - onSubmit(): void { - this._userService.registerUserRequest(this.registerUserFormGroup).subscribe( - res => { - this._tokenService.setUserTokenToSessionStorage(res); - this._router.navigate(['/']); - }, - (err: HttpErrorResponse) => { - this._errorBar.showError(err); - } - ); - } - onRedirectLogin(): void { - this._router.navigate(['/login']); - } - - get firstName(): AbstractControl | null { - return this.registerUserFormGroup.get('firstName'); - } - - get lastName(): AbstractControl | null { - return this.registerUserFormGroup.get('lastName'); - } - - get username(): AbstractControl | null { - return this.registerUserFormGroup.get('username'); - } - - get email(): AbstractControl | null { - return this.registerUserFormGroup.get('email'); - } - - get password(): AbstractControl | null { - return this.registerUserFormGroup.get('password'); - } -} diff --git a/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.css b/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.css deleted file mode 100644 index bee634d..0000000 --- a/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.css +++ /dev/null @@ -1,11 +0,0 @@ -#success-bar { - width: 100%; - background-color: var(--success); - color: white; - padding: .2em; - text-align: center; -} - -#success-bar:empty { - display: none; -} diff --git a/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.html b/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.html deleted file mode 100644 index 026e955..0000000 --- a/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.html +++ /dev/null @@ -1 +0,0 @@ -<div id="success-bar">{{successMsg}}</div> diff --git a/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.ts b/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.ts deleted file mode 100644 index f7c7e54..0000000 --- a/src/DevHive.Angular/src/app/components/success-bar/success-bar.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Component, OnInit } from '@angular/core'; - -@Component({ - selector: 'app-success-bar', - templateUrl: './success-bar.component.html', - styleUrls: ['./success-bar.component.css'] -}) -export class SuccessBarComponent implements OnInit { - public successMsg = ''; - - constructor() - { } - - ngOnInit(): void { - this.hideMsg(); - } - - showMsg(msg?: string | undefined): void { - if (msg === undefined) { - this.successMsg = 'Success!'; - } - else if (msg.trim() === '') { - this.successMsg = 'Success!'; - } - else { - this.successMsg = msg; - } - } - - hideMsg(): void { - this.successMsg = ''; - } -} diff --git a/src/DevHive.Angular/src/app/services/cloudinary.service.ts b/src/DevHive.Angular/src/app/services/cloudinary.service.ts deleted file mode 100644 index 999e498..0000000 --- a/src/DevHive.Angular/src/app/services/cloudinary.service.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {HttpClient} from '@angular/common/http'; -import {Injectable} from '@angular/core'; -import {Observable} from 'rxjs'; - -@Injectable({ - providedIn: 'root' -}) -export class CloudinaryService { - constructor(private _http: HttpClient) - { } - - getFileRequest(fileLink: string): Observable<Blob> { - return this._http.get(fileLink, { - responseType: 'blob' - }); - } -} diff --git a/src/DevHive.Angular/src/app/services/comment.service.ts b/src/DevHive.Angular/src/app/services/comment.service.ts deleted file mode 100644 index c9dbf35..0000000 --- a/src/DevHive.Angular/src/app/services/comment.service.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Guid } from 'guid-typescript'; -import { Observable } from 'rxjs'; -import { Comment } from 'src/models/comment'; -import { AppConstants } from '../app-constants.module'; -import { TokenService } from './token.service'; - -@Injectable({ - providedIn: 'root' -}) -export class CommentService { - constructor(private _http: HttpClient, private _tokenService: TokenService) - { } - - getDefaultComment(): Comment { - return new Comment(Guid.createEmpty(), Guid.createEmpty(), 'Gosho', 'Trapov', 'gosho_trapov', 'Your opinion on my idea?', new Date()); - } - - /* Requests from session storage */ - - createCommentWithSessionStorageRequest(postId: Guid, commentMessage: string): Observable<object> { - const userId = this._tokenService.getUserIdFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.createCommentRequest(userId, token, postId, commentMessage); - } - - putCommentWithSessionStorageRequest(commentId: Guid, postId: Guid, newMessage: string): Observable<object> { - const userId = this._tokenService.getUserIdFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.putCommentRequest(userId, token, commentId, postId, newMessage); - } - - deleteCommentWithSessionStorage(commentId: Guid): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.deleteCommentRequest(commentId, token); - } - - /* Comment requests */ - - createCommentRequest(userId: Guid, authToken: string, postId: Guid, commentMessage: string): Observable<object> { - const body = { - postId: postId.toString(), - message: commentMessage - }; - const options = { - params: new HttpParams().set('UserId', userId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.post(AppConstants.API_COMMENT_URL, body, options); - } - - getCommentRequest(id: Guid): Observable<object> { - const options = { - params: new HttpParams().set('Id', id.toString()) - }; - return this._http.get(AppConstants.API_COMMENT_URL, options); - } - - putCommentRequest(userId: Guid, authToken: string, commentId: Guid, postId: Guid, newMessage: string): Observable<object> { - const body = { - commentId: commentId.toString(), - postId: postId.toString(), - newMessage: newMessage - }; - const options = { - params: new HttpParams().set('UserId', userId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.put(AppConstants.API_COMMENT_URL, body, options); - } - - deleteCommentRequest(commentId: Guid, authToken: string): Observable<object> { - const options = { - params: new HttpParams().set('Id', commentId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.delete(AppConstants.API_COMMENT_URL, options); - } -} diff --git a/src/DevHive.Angular/src/app/services/feed.service.ts b/src/DevHive.Angular/src/app/services/feed.service.ts deleted file mode 100644 index d160f6d..0000000 --- a/src/DevHive.Angular/src/app/services/feed.service.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Guid } from 'guid-typescript'; -import { Observable } from 'rxjs'; -import { AppConstants } from '../app-constants.module'; -import { TokenService } from './token.service'; - -@Injectable({ - providedIn: 'root' -}) -export class FeedService { - constructor(private _http: HttpClient, private _tokenService: TokenService) - { } - - /* Requests from session storage */ - - getUserFeedFromSessionStorageRequest(pageNumber: number, firstTimeIssued: string, pageSize: number): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - const userId = this._tokenService.getUserIdFromSessionStorageToken(); - - return this.getUserFeedRequest(userId, token, pageNumber, firstTimeIssued, pageSize); - } - - /* Feed requests */ - - getUserFeedRequest(userId: Guid, authToken: string, pageNumber: number, firstTimeIssued: string, pageSize: number): Observable<object> { - const body = { - pageNumber: pageNumber, - firstPageTimeIssued: firstTimeIssued, - pageSize: pageSize - }; - const options = { - params: new HttpParams().set('UserId', userId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.post(AppConstants.API_FEED_URL + '/GetPosts', body, options); - } - - getUserPostsRequest(userName: string, pageNumber: number, firstTimeIssued: string, pageSize: number): Observable<object> { - const body = { - pageNumber: pageNumber, - firstPageTimeIssued: firstTimeIssued, - pageSize: pageSize - }; - const options = { - params: new HttpParams().set('UserName', userName) - }; - return this._http.post(AppConstants.API_FEED_URL + '/GetUserPosts', body, options); - } -} diff --git a/src/DevHive.Angular/src/app/services/language.service.ts b/src/DevHive.Angular/src/app/services/language.service.ts deleted file mode 100644 index 15e241f..0000000 --- a/src/DevHive.Angular/src/app/services/language.service.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Guid } from 'guid-typescript'; -import { Observable } from 'rxjs'; -import { Language } from 'src/models/language'; -import { AppConstants } from '../app-constants.module'; -import { TokenService } from './token.service'; - -@Injectable({ - providedIn: 'root' -}) -export class LanguageService { - constructor(private _http: HttpClient, private _tokenService: TokenService) - { } - - /* Requests from session storage */ - - createLanguageWithSessionStorageRequest(name: string): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.createLanguageRequest(name, token); - } - - getAllLanguagesWithSessionStorageRequest(): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.getAllLanguagesRequest(token); - } - - putLanguageWithSessionStorageRequest(langId: Guid, newName: string): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.putLanguageRequest(token, langId, newName); - } - - deleteLanguageWithSessionStorageRequest(langId: Guid): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.deleteLanguageRequest(token, langId); - } - - /* Language requests */ - - createLanguageRequest(name: string, authToken: string): Observable<object> { - const body = { - name: name - }; - const options = { - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.post(AppConstants.API_LANGUAGE_URL, body, options); - } - - getLanguageRequest(langId: Guid): Observable<object> { - const options = { - params: new HttpParams().set('Id', langId.toString()), - }; - return this._http.get(AppConstants.API_LANGUAGE_URL, options); - } - - getAllLanguagesRequest(authToken: string): Observable<object> { - const options = { - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.get(AppConstants.API_LANGUAGE_URL + '/GetLanguages', options); - } - - getFullLanguagesFromIncomplete(givenLanguages: Language[]): Promise<Language[]> { - if (givenLanguages.length === 0) { - return new Promise(resolve => resolve(givenLanguages)); - } - - // This accepts language array with incomplete languages, meaning - // languages that only have an id, but no name - return new Promise(resolve => { - const lastGuid = givenLanguages[givenLanguages.length - 1].id; - - // For each language, request his name and assign it - for (const lang of givenLanguages) { - this.getLanguageRequest(lang.id).subscribe( - (result: object) => { - // this only assigns the "name" property to the language, - // because only the name is returned from the request - Object.assign(lang, result); - - if (lastGuid === lang.id) { - resolve(givenLanguages); - } - } - ); - } - }); - } - - putLanguageRequest(authToken: string, langId: Guid, newName: string): Observable<object> { - const body = { - name: newName - }; - const options = { - params: new HttpParams().set('Id', langId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.put(AppConstants.API_LANGUAGE_URL, body, options); - } - - deleteLanguageRequest(authToken: string, langId: Guid): Observable<object> { - const options = { - params: new HttpParams().set('Id', langId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.delete(AppConstants.API_LANGUAGE_URL, options); - } -} diff --git a/src/DevHive.Angular/src/app/services/post.service.ts b/src/DevHive.Angular/src/app/services/post.service.ts deleted file mode 100644 index 7b2a539..0000000 --- a/src/DevHive.Angular/src/app/services/post.service.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import * as FormData from 'form-data'; -import { Guid } from 'guid-typescript'; -import { Observable } from 'rxjs'; -import { Post } from 'src/models/post'; -import { AppConstants } from '../app-constants.module'; -import { TokenService } from './token.service'; - -@Injectable({ - providedIn: 'root' -}) -export class PostService { - constructor(private _http: HttpClient, private _tokenService: TokenService) - { } - - getDefaultPost(): Post { - return new Post(Guid.createEmpty(), 'Gosho', 'Trapov', 'gosho_trapov', 'Your opinion on my idea?', new Date(), [], []); - } - - /* Requests from session storage */ - - createPostWithSessionStorageRequest(postMessage: string, files: File[]): Observable<object> { - const userId = this._tokenService.getUserIdFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.createPostRequest(userId, token, postMessage, files); - } - - putPostWithSessionStorageRequest(postId: Guid, newMessage: string, posts: File[]): Observable<object> { - const userId = this._tokenService.getUserIdFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.putPostRequest(userId, token, postId, newMessage, posts); - } - - deletePostWithSessionStorage(postId: Guid): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.deletePostRequest(postId, token); - } - - /* Post requests */ - - createPostRequest(userId: Guid, authToken: string, postMessage: string, files: File[]): Observable<object> { - const form = new FormData(); - form.append('message', postMessage); - for (const file of files) { - form.append('files', file, file.name); - } - const options = { - params: new HttpParams().set('UserId', userId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.post(AppConstants.API_POST_URL, form, options); - } - - getPostRequest(id: Guid): Observable<object> { - const options = { - params: new HttpParams().set('Id', id.toString()) - }; - return this._http.get(AppConstants.API_POST_URL, options); - } - - putPostRequest(userId: Guid, authToken: string, postId: Guid, newMessage: string, files: File[]): Observable<object> { - const form = new FormData(); - form.append('postId', postId); - form.append('newMessage', newMessage); - for (const file of files) { - form.append('files', file, file.name); - } - const options = { - params: new HttpParams().set('UserId', userId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.put(AppConstants.API_POST_URL, form, options); - } - - deletePostRequest(postId: Guid, authToken: string): Observable<object> { - const options = { - params: new HttpParams().set('Id', postId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.delete(AppConstants.API_POST_URL, options); - } -} diff --git a/src/DevHive.Angular/src/app/services/technology.service.ts b/src/DevHive.Angular/src/app/services/technology.service.ts deleted file mode 100644 index dbdc039..0000000 --- a/src/DevHive.Angular/src/app/services/technology.service.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { Guid } from 'guid-typescript'; -import { Observable } from 'rxjs'; -import { Technology } from 'src/models/technology'; -import { AppConstants } from '../app-constants.module'; -import { TokenService } from './token.service'; - -@Injectable({ - providedIn: 'root' -}) -export class TechnologyService { - constructor(private _http: HttpClient, private _tokenService: TokenService) - { } - - /* Requests from session storage */ - - createTechnologyWithSessionStorageRequest(name: string): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.createtTechnologyRequest(name, token); - } - - getAllTechnologiesWithSessionStorageRequest(): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.getAllTechnologiesRequest(token); - } - - putTechnologyWithSessionStorageRequest(langId: Guid, newName: string): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.putTechnologyRequest(token, langId, newName); - } - - deleteTechnologyWithSessionStorageRequest(langId: Guid): Observable<object> { - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.deleteTechnologyRequest(token, langId); - } - - /* Technology requests */ - - createtTechnologyRequest(name: string, authToken: string): Observable<object> { - const body = { - name: name - }; - const options = { - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.post(AppConstants.API_TECHNOLOGY_URL, body, options); - } - - getTechnologyRequest(techId: Guid): Observable<object> { - const options = { - params: new HttpParams().set('Id', techId.toString()) - }; - return this._http.get(AppConstants.API_TECHNOLOGY_URL, options); - } - - getAllTechnologiesRequest(authToken: string): Observable<object> { - const options = { - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.get(AppConstants.API_TECHNOLOGY_URL + '/GetTechnologies', options); - } - - getFullTechnologiesFromIncomplete(givenTechnologies: Technology[]): Promise<Technology[]> { - if (givenTechnologies.length === 0) { - return new Promise(resolve => resolve(givenTechnologies)); - } - - // This accepts language array with incomplete languages, meaning - // languages that only have an id, but no name - return new Promise(resolve => { - const lastGuid = givenTechnologies[givenTechnologies.length - 1].id; - - // For each language, request his name and assign it - for (const tech of givenTechnologies) { - this.getTechnologyRequest(tech.id).subscribe( - (result: object) => { - // this only assigns the "name" property to the language, - // because only the name is returned from the request - Object.assign(tech, result); - - if (lastGuid === tech.id) { - resolve(givenTechnologies); - } - } - ); - } - }); - } - - putTechnologyRequest(authToken: string, langId: Guid, newName: string): Observable<object> { - const body = { - name: newName - }; - const options = { - params: new HttpParams().set('Id', langId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.put(AppConstants.API_TECHNOLOGY_URL, body, options); - } - - deleteTechnologyRequest(authToken: string, langId: Guid): Observable<object> { - const options = { - params: new HttpParams().set('Id', langId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.delete(AppConstants.API_TECHNOLOGY_URL, options); - } -} diff --git a/src/DevHive.Angular/src/app/services/token.service.ts b/src/DevHive.Angular/src/app/services/token.service.ts deleted file mode 100644 index 62bc07e..0000000 --- a/src/DevHive.Angular/src/app/services/token.service.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Guid } from 'guid-typescript'; -import jwt_decode from 'jwt-decode'; -import { IJWTPayload } from 'src/interfaces/jwt-payload'; -import { IUserCredentials } from 'src/interfaces/user-credentials'; -import { AppConstants } from '../app-constants.module'; - -@Injectable({ - providedIn: 'root' -}) -export class TokenService { - constructor() - { } - - /* Session storage */ - - setUserTokenToSessionStorage(response: object): void { - const token = JSON.stringify(response); - sessionStorage.setItem(AppConstants.SESSION_TOKEN_KEY, token.substr(10, token.length - 12)); - } - - getTokenFromSessionStorage(): string { - return sessionStorage.getItem(AppConstants.SESSION_TOKEN_KEY) ?? ''; - } - - getUserIdFromSessionStorageToken(): Guid { - const jwt: IJWTPayload = { - token: this.getTokenFromSessionStorage() - }; - const userCred = jwt_decode<IUserCredentials>(jwt.token); - - return userCred.ID; - } - - getUsernameFromSessionStorageToken(): string { - const jwt: IJWTPayload = { - token: this.getTokenFromSessionStorage() - }; - const userCred = jwt_decode<IUserCredentials>(jwt.token); - - return userCred.Username; - } - - logoutUserFromSessionStorage(): void { - sessionStorage.removeItem(AppConstants.SESSION_TOKEN_KEY); - } -} diff --git a/src/DevHive.Angular/src/app/services/user.service.ts b/src/DevHive.Angular/src/app/services/user.service.ts deleted file mode 100644 index 31862c4..0000000 --- a/src/DevHive.Angular/src/app/services/user.service.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Guid } from 'guid-typescript'; -import { User } from '../../models/identity/user'; -import { FormGroup } from '@angular/forms'; -import { AppConstants } from 'src/app/app-constants.module'; -import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { Role } from 'src/models/identity/role'; -import { Friend } from 'src/models/identity/friend'; -import { TokenService } from './token.service'; - -@Injectable({ - providedIn: 'root' -}) -export class UserService { - constructor(private _http: HttpClient, private _tokenService: TokenService) - { } - - getDefaultUser(): User { - return new User(Guid.createEmpty(), 'gosho_trapov', 'Gosho', 'Trapov', 'gotra@bg.com', AppConstants.FALLBACK_PROFILE_ICON, [], [], [], []); - } - - /* Requests from session storage */ - - getUserFromSessionStorageRequest(): Observable<object> { - const userId = this._tokenService.getUserIdFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.getUserRequest(userId, token); - } - - addFriendToUserFromSessionStorageRequest(newFriendUserName: string): Observable<object> { - const userUserName = this._tokenService.getUsernameFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.addFriendToUserRequest(userUserName, token, newFriendUserName); - } - - putUserFromSessionStorageRequest(updateUserFormGroup: FormGroup, userRoles: Role[], userFriends: Friend[]): Observable<object> { - const userId = this._tokenService.getUserIdFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.putUserRequest(userId, token, updateUserFormGroup, userRoles, userFriends); - } - - putProfilePictureFromSessionStorageRequest(newPicture: File): Observable<object> { - const userId = this._tokenService.getUserIdFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.putProfilePictureRequest(userId, token, newPicture); - } - - putBareUserFromSessionStorageRequest(user: User, password: string): Observable<object> { - const userId = this._tokenService.getUserIdFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.putBareUserRequest(userId, token, user, password); - } - - deleteUserFromSessionStorageRequest(): Observable<object> { - const userId = this._tokenService.getUserIdFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.deleteUserRequest(userId, token); - } - - removeFriendFromUserFromSessionStorageRequest(friendToRemoveUserName: string): Observable<object> { - const userUserName = this._tokenService.getUsernameFromSessionStorageToken(); - const token = this._tokenService.getTokenFromSessionStorage(); - - return this.removeFriendFromUserRequest(userUserName, token, friendToRemoveUserName); - } - - - /* User requests */ - - loginUserRequest(loginUserFormGroup: FormGroup): Observable<object> { - const body = { - UserName: loginUserFormGroup.get('username')?.value, - Password: loginUserFormGroup.get('password')?.value - }; - return this._http.post(AppConstants.API_USER_LOGIN_URL, body); - } - - registerUserRequest(registerUserFormGroup: FormGroup): Observable<object> { - const body = { - UserName: registerUserFormGroup.get('username')?.value, - Email: registerUserFormGroup.get('email')?.value, - FirstName: registerUserFormGroup.get('firstName')?.value, - LastName: registerUserFormGroup.get('lastName')?.value, - Password: registerUserFormGroup.get('password')?.value - }; - return this._http.post(AppConstants.API_USER_REGISTER_URL, body); - } - - addFriendToUserRequest(userUserName: string, authToken: string, newFriendUserName: string): Observable<object> { - const body = { - newFriendUserName: newFriendUserName - }; - const options = { - params: new HttpParams().set('UserName', userUserName), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.put(AppConstants.API_USER_URL + '/AddFriend', body, options); - } - - getUserRequest(userId: Guid, authToken: string): Observable<object> { - const options = { - params: new HttpParams().set('Id', userId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.get(AppConstants.API_USER_URL, options); - } - - getUserByUsernameRequest(username: string): Observable<object> { - const options = { - params: new HttpParams().set('UserName', username), - }; - return this._http.get(AppConstants.API_USER_URL + '/GetUser', options); - } - - putUserRequest(userId: Guid, authToken: string, updateUserFormGroup: FormGroup, userRoles: Role[], userFriends: Friend[]): Observable<object> { - const body = { - UserName: updateUserFormGroup.get('username')?.value, - Email: updateUserFormGroup.get('email')?.value, - FirstName: updateUserFormGroup.get('firstName')?.value, - LastName: updateUserFormGroup.get('lastName')?.value, - Password: updateUserFormGroup.get('password')?.value, - Roles: userRoles, - Friends: userFriends, - Languages: updateUserFormGroup.get('languages')?.value, - Technologies: updateUserFormGroup.get('technologies')?.value - }; - const options = { - params: new HttpParams().set('Id', userId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.put(AppConstants.API_USER_URL, body, options); - } - - putBareUserRequest(userId: Guid, authToken: string, user: User, password: string): Observable<object> { - const body: object = user; - Object.assign(body, { password: password }); - const options = { - params: new HttpParams().set('Id', userId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.put(AppConstants.API_USER_URL, body, options); - } - - putProfilePictureRequest(userId: Guid, authToken: string, newPicture: File): Observable<object> { - const form = new FormData(); - form.append('picture', newPicture); - const options = { - params: new HttpParams().set('UserId', userId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.put(AppConstants.API_USER_URL + '/ProfilePicture', form, options); - } - - deleteUserRequest(userId: Guid, authToken: string): Observable<object> { - const options = { - params: new HttpParams().set('Id', userId.toString()), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.delete(AppConstants.API_USER_URL, options); - } - - removeFriendFromUserRequest(userUserName: string, authToken: string, friendToRemoveUserName: string): Observable<object> { - const body = { - friendUserNameToRemove: friendToRemoveUserName - }; - const options = { - params: new HttpParams().set('UserName', userUserName), - headers: new HttpHeaders().set('Authorization', 'Bearer ' + authToken) - }; - return this._http.post(AppConstants.API_USER_URL + '/RemoveFriend', body, options); - } -} diff --git a/src/DevHive.Angular/src/assets/.gitkeep b/src/DevHive.Angular/src/assets/.gitkeep deleted file mode 100644 index e69de29..0000000 --- a/src/DevHive.Angular/src/assets/.gitkeep +++ /dev/null diff --git a/src/DevHive.Angular/src/assets/images/comment.png b/src/DevHive.Angular/src/assets/images/comment.png Binary files differdeleted file mode 100644 index 5f8e8d9..0000000 --- a/src/DevHive.Angular/src/assets/images/comment.png +++ /dev/null diff --git a/src/DevHive.Angular/src/assets/images/feed/chat-pic.png b/src/DevHive.Angular/src/assets/images/feed/chat-pic.png Binary files differdeleted file mode 100644 index 60241fa..0000000 --- a/src/DevHive.Angular/src/assets/images/feed/chat-pic.png +++ /dev/null diff --git a/src/DevHive.Angular/src/assets/images/feed/profile-pic.png b/src/DevHive.Angular/src/assets/images/feed/profile-pic.png Binary files differdeleted file mode 100644 index 87f67f5..0000000 --- a/src/DevHive.Angular/src/assets/images/feed/profile-pic.png +++ /dev/null diff --git a/src/DevHive.Angular/src/assets/images/paper-clip.png b/src/DevHive.Angular/src/assets/images/paper-clip.png Binary files differdeleted file mode 100644 index 46ce0a7..0000000 --- a/src/DevHive.Angular/src/assets/images/paper-clip.png +++ /dev/null diff --git a/src/DevHive.Angular/src/environments/environment.prod.ts b/src/DevHive.Angular/src/environments/environment.prod.ts deleted file mode 100644 index 3612073..0000000 --- a/src/DevHive.Angular/src/environments/environment.prod.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const environment = { - production: true -}; diff --git a/src/DevHive.Angular/src/environments/environment.ts b/src/DevHive.Angular/src/environments/environment.ts deleted file mode 100644 index 7b4f817..0000000 --- a/src/DevHive.Angular/src/environments/environment.ts +++ /dev/null @@ -1,16 +0,0 @@ -// This file can be replaced during build by using the `fileReplacements` array. -// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. -// The list of file replacements can be found in `angular.json`. - -export const environment = { - production: false -}; - -/* - * For easier debugging in development mode, you can import the following file - * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. - * - * This import should be commented out in production mode because it will have a negative impact - * on performance if an error is thrown. - */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. diff --git a/src/DevHive.Angular/src/favicon.ico b/src/DevHive.Angular/src/favicon.ico Binary files differdeleted file mode 100644 index 997406a..0000000 --- a/src/DevHive.Angular/src/favicon.ico +++ /dev/null diff --git a/src/DevHive.Angular/src/index.html b/src/DevHive.Angular/src/index.html deleted file mode 100644 index d488ca9..0000000 --- a/src/DevHive.Angular/src/index.html +++ /dev/null @@ -1,16 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <meta charset="utf-8"> - <base href="/"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <link rel="icon" type="image/x-icon" href="favicon.ico"> - <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet"> - <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> -</head> - -<body class="mat-typography"> - <app-root></app-root> -</body> - -</html>
\ No newline at end of file diff --git a/src/DevHive.Angular/src/interfaces/api-error.ts b/src/DevHive.Angular/src/interfaces/api-error.ts deleted file mode 100644 index 4dd68f3..0000000 --- a/src/DevHive.Angular/src/interfaces/api-error.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface IApiError { - type: string; - title: string; - status: number; - traceId: string; -} diff --git a/src/DevHive.Angular/src/interfaces/jwt-payload.ts b/src/DevHive.Angular/src/interfaces/jwt-payload.ts deleted file mode 100644 index a2d0f0d..0000000 --- a/src/DevHive.Angular/src/interfaces/jwt-payload.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IJWTPayload { - token: string; -} diff --git a/src/DevHive.Angular/src/interfaces/user-credentials.ts b/src/DevHive.Angular/src/interfaces/user-credentials.ts deleted file mode 100644 index bb47540..0000000 --- a/src/DevHive.Angular/src/interfaces/user-credentials.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Guid } from 'guid-typescript'; - -export interface IUserCredentials { - ID: Guid; - Username: string; -} diff --git a/src/DevHive.Angular/src/main.ts b/src/DevHive.Angular/src/main.ts deleted file mode 100644 index c7b673c..0000000 --- a/src/DevHive.Angular/src/main.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app/app.module'; -import { environment } from './environments/environment'; - -if (environment.production) { - enableProdMode(); -} - -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); diff --git a/src/DevHive.Angular/src/models/comment.ts b/src/DevHive.Angular/src/models/comment.ts deleted file mode 100644 index 0d1755f..0000000 --- a/src/DevHive.Angular/src/models/comment.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Guid } from 'guid-typescript'; - -export class Comment { - private _commentId: Guid; - private _postId: Guid; - private _issuerFirstName: string; - private _issuerLastName: string; - private _issuerUsername: string; - private _message: string; - private _timeCreated: Date; - - constructor(commentId: Guid, postId: Guid, issuerFirstName: string, issuerLastName: string, issuerUsername: string, message: string, timeCreated: Date) { - this.commentId = commentId; - this.postId = postId; - this.issuerFirstName = issuerFirstName; - this.issuerLastName = issuerLastName; - this.issuerUsername = issuerUsername; - this.message = message; - this.timeCreated = timeCreated; - } - - public get commentId(): Guid { - return this._commentId; - } - public set commentId(v: Guid) { - this._commentId = v; - } - - public get postId(): Guid { - return this._postId; - } - public set postId(v: Guid) { - this._postId = v; - } - - public get issuerFirstName(): string { - return this._issuerFirstName; - } - public set issuerFirstName(v: string) { - this._issuerFirstName = v; - } - - public get issuerLastName(): string { - return this._issuerLastName; - } - public set issuerLastName(v: string) { - this._issuerLastName = v; - } - - public get issuerUsername(): string { - return this._issuerUsername; - } - public set issuerUsername(v: string) { - this._issuerUsername = v; - } - - public get message(): string { - return this._message; - } - public set message(v: string) { - this._message = v; - } - - public get timeCreated(): Date { - return this._timeCreated; - } - public set timeCreated(v: Date) { - this._timeCreated = v; - } -} diff --git a/src/DevHive.Angular/src/models/identity/friend.ts b/src/DevHive.Angular/src/models/identity/friend.ts deleted file mode 100644 index 22290cd..0000000 --- a/src/DevHive.Angular/src/models/identity/friend.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Friend { - public userName: string; -} diff --git a/src/DevHive.Angular/src/models/identity/role.ts b/src/DevHive.Angular/src/models/identity/role.ts deleted file mode 100644 index 132b0b0..0000000 --- a/src/DevHive.Angular/src/models/identity/role.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Role { - public name: string; -} diff --git a/src/DevHive.Angular/src/models/identity/user.ts b/src/DevHive.Angular/src/models/identity/user.ts deleted file mode 100644 index e0038e0..0000000 --- a/src/DevHive.Angular/src/models/identity/user.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Guid } from 'guid-typescript'; -import { Language } from '../language'; -import { Technology } from '../technology'; -import { Friend } from './friend'; -import { Role } from './role'; - -export class User { - private _id : Guid; - private _lastName : string; - private _firstName : string; - private _userName : string; - private _email: string; - private _profilePictureURL : string; - private _languages: Language[]; - private _technologies: Technology[]; - private _roles: Role[]; - private _friends: Friend[]; - - constructor(id: Guid, userName: string, firstName: string, lastName: string, email: string, profilePictureURL: string, languages: Language[], technologies: Technology[], roles: Role[], friends: Friend[]) { - this.id = id; - this.userName = userName; - this.firstName = firstName; - this.lastName = lastName; - this.email = email; - this._profilePictureURL = profilePictureURL; - this.languages = languages; - this.technologies = technologies; - this.roles = roles; - } - - public get id(): Guid { - return this._id; - } - public set id(v: Guid) { - this._id = v; - } - - public get userName(): string { - return this._userName; - } - public set userName(v: string) { - this._userName = v; - } - - public get firstName(): string { - return this._firstName; - } - public set firstName(v: string) { - this._firstName = v; - } - - public get lastName(): string { - return this._lastName; - } - public set lastName(v: string) { - this._lastName = v; - } - - public get email(): string { - return this._email; - } - public set email(v: string) { - this._email = v; - } - - public get profilePictureURL(): string { - return this._profilePictureURL; - } - public set profilePictureURL(v: string) { - this._profilePictureURL = v; - } - - public get languages(): Language[] { - return this._languages; - } - public set languages(v: Language[]) { - this._languages = v; - } - - public get technologies(): Technology[] { - return this._technologies; - } - public set technologies(v: Technology[]) { - this._technologies = v; - } - - public get roles(): Role[] { - return this._roles; - } - public set roles(v: Role[]) { - this._roles = v; - } - - public get friends(): Friend[] { - return this._friends; - } - public set friends(v: Friend[]) { - this._friends = v; - } -} diff --git a/src/DevHive.Angular/src/models/language.ts b/src/DevHive.Angular/src/models/language.ts deleted file mode 100644 index e3aa61e..0000000 --- a/src/DevHive.Angular/src/models/language.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Guid } from 'guid-typescript'; - -export class Language { - public id: Guid; - public name: string; -} diff --git a/src/DevHive.Angular/src/models/post-comment.ts b/src/DevHive.Angular/src/models/post-comment.ts deleted file mode 100644 index 5d1e346..0000000 --- a/src/DevHive.Angular/src/models/post-comment.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Guid } from 'guid-typescript'; - -export class PostComment { - public id: Guid; -} diff --git a/src/DevHive.Angular/src/models/post.ts b/src/DevHive.Angular/src/models/post.ts deleted file mode 100644 index 8e58bea..0000000 --- a/src/DevHive.Angular/src/models/post.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Guid } from 'guid-typescript'; -import { Comment } from './comment'; -import { PostComment } from './post-comment'; - -export class Post { - private _postId: Guid; - private _creatorFirstName: string; - private _creatorLastName: string; - private _creatorUsername: string; - private _message: string; - private _timeCreated: Date; - private _comments: PostComment[]; - private _fileURLs: string[]; - - constructor(postId: Guid, creatorFirstName: string, creatorLastName: string, creatorUsername: string, message: string, timeCreated: Date, comments: PostComment[], fileURLs: string[]) { - this.postId = postId; - this.creatorFirstName = creatorFirstName; - this.creatorLastName = creatorLastName; - this.creatorUsername = creatorUsername; - this.message = message; - this.timeCreated = timeCreated; - this.comments = comments; - this.fileURLs = fileURLs; - } - - public get postId(): Guid { - return this._postId; - } - public set postId(v: Guid) { - this._postId = v; - } - - public get creatorFirstName(): string { - return this._creatorFirstName; - } - public set creatorFirstName(v: string) { - this._creatorFirstName = v; - } - - public get creatorLastName(): string { - return this._creatorLastName; - } - public set creatorLastName(v: string) { - this._creatorLastName = v; - } - - public get creatorUsername(): string { - return this._creatorUsername; - } - public set creatorUsername(v: string) { - this._creatorUsername = v; - } - - public get message(): string { - return this._message; - } - public set message(v: string) { - this._message = v; - } - - public get timeCreated(): Date { - return this._timeCreated; - } - public set timeCreated(v: Date) { - this._timeCreated = v; - } - - public get comments(): PostComment[] { - return this._comments; - } - public set comments(v: PostComment[]) { - this._comments = v; - } - - public get fileURLs(): string[] { - return this._fileURLs; - } - public set fileURLs(v: string[]) { - this._fileURLs = v; - } -} diff --git a/src/DevHive.Angular/src/models/technology.ts b/src/DevHive.Angular/src/models/technology.ts deleted file mode 100644 index 1869d14..0000000 --- a/src/DevHive.Angular/src/models/technology.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Guid } from 'guid-typescript'; - -export class Technology { - public id: Guid; - public name: string; -} diff --git a/src/DevHive.Angular/src/polyfills.ts b/src/DevHive.Angular/src/polyfills.ts deleted file mode 100644 index 9b8f300..0000000 --- a/src/DevHive.Angular/src/polyfills.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), - * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** IE11 requires the following for NgClass support on SVG elements */ -// import 'classlist.js'; // Run `npm install --save classlist.js`. - -/** - * Web Animations `@angular/platform-browser/animations` - * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. - * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). - */ -// import 'web-animations-js'; // Run `npm install --save web-animations-js`. - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ diff --git a/src/DevHive.Angular/src/reset.css b/src/DevHive.Angular/src/reset.css deleted file mode 100644 index d9ac20b..0000000 --- a/src/DevHive.Angular/src/reset.css +++ /dev/null @@ -1,52 +0,0 @@ -/* http://meyerweb.com/eric/tools/css/reset/ - v5.0.1 | 20191019 - License: none (public domain) -*/ - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, menu, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -main, menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, main, menu, nav, section { - display: block; -} -/* HTML5 hidden-attribute fix for newer browsers */ -*[hidden] { - display: none; -} -body { - line-height: 1; -} -menu, ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/src/DevHive.Angular/src/styles.css b/src/DevHive.Angular/src/styles.css deleted file mode 100644 index eeb93fe..0000000 --- a/src/DevHive.Angular/src/styles.css +++ /dev/null @@ -1,262 +0,0 @@ -/* You can add global styles to this file, and also import other style files */ -@import "./reset.css"; - -:root { - --bg-color: white; - --focus-color: forestgreen; - --card-bg: white; - --success: forestgreen; - --failure: indianred; -} - -html, body { - height: 100%; - margin: 0; -} -body { - font: 21px sans-serif !important; - background-color: var(--bg-color); -} - -input:focus, button:focus { - outline: 0; -} - -#content { /* Used for the login and register pages */ - height: 100%; - max-width: 20em; - box-sizing: border-box; - border: .5em solid var(--bg-color); - - margin: 0 auto; - - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; -} - -.rounded-border { - border: 2px solid black; - border-radius: .6em; - padding: .4em; -} - -.round-image { - border-radius: 50%; - object-fit: cover; -} - -.title { - font-size: 2em; - font-weight: bold; -} - -.error { - color: red; -} - -.scroll-standalone { - width: 100%; - max-height: 100%; - overflow-y: auto; -} - - /* Hide scrollbar for Chrome, Safari and Opera */ -.scroll-standalone::-webkit-scrollbar { - display: none; -} - - /* Hide scrollbar for IE, Edge and Firefox */ -.scroll-standalone { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ -} - -.user-language, .user-technology { - border-radius: .4em; - background-color: lightgrey; - padding: .26em; - margin: .1em .2em; - width: fit-content; -} - -/* Inputs, the type found in login and register */ - -.input-selection { - position: relative; - margin-top: .7em; -} - - /* Don't show the placeholder when the label is on top - */ -.input-selection .input-field::-webkit-input-placeholder { - opacity: 0; -} - -.input-field { - width: 100%; - background-color: var(--bg-color); - - border: 0; - border-bottom: 1px solid grey; - box-sizing: border-box; - - margin-bottom: .5em; - padding: .4em; - padding-left: 0; - - font-size: inherit; -} - -.input-field-label { - width: inherit; - height: inherit; - position: absolute; - left: 0; - - margin-top: .4em; - color: grey; -} - - /* When hovering, typing or having typed something in an input, - * make the label smaller, color it and then move it up - */ -.input-selection:hover > .input-field-label , -.input-selection > input:not(:placeholder-shown) + .input-field-label , -.input-selection > input:focus + .input-field-label { - font-size: .7em; - color: var(--focus-color); - transform: translate(0, -1.2em); -} - - /* Show the placeholder, when you've hovered or - * focused (typing in) on the input-field - */ -.input-selection:hover > .input-field::-webkit-input-placeholder, -.input-selection > .input-field:focus::-webkit-input-placeholder { - opacity: 1; -} - - /* Make the underline thicker and change it's and the cursors's - * color when hovered or focused (typing in) on the input-field - */ -.input-selection:hover > .input-field, -.input-field:focus { - border-color: var(--focus-color) !important; - caret-color: var(--focus-color); - border-width: 2px !important; - margin-top: -1px !important; -} - - -/* Input errors */ - -.input-errors { - margin-top: -.8em; - font-size: .7em; - - position: absolute; - right: 0; - top: 0; -} - - /* Move the errors above the input when - * using the site on a small screen and - * add some space for them above the input - */ -@media screen and (max-width: 350px) { - .input-errors { - margin-top: -1.8em; - } - .input-selection { - margin-top: 1.6em; - } -} - -.input-errors > .error { - margin-left: .3em; -} - -.input-field:focus ~ .input-errors > .error { - opacity: 1 !important; -} - -.input-field:placeholder-shown ~ .input-errors > .error { - opacity: 0; -} - -/* Submit button */ - -.submit-btn { - width: 100%; - color: white; - background-color: black; - - border: 2px solid black; - border-radius: .4em; - box-sizing: border-box; - - font-size: .8em; - text-align: center; - - margin-top: .5em; - padding: .3em; -} - -.submit-btn:hover { - cursor: pointer; - color: var(--focus-color); - background-color: white; - border-color: var(--focus-color) !important; -} - -.submit-btn:active { - transition: 0s; - transform: scale(.9); -} - -.delete-btn:hover { - color: indianred; - border-color: indianred !important; -} - -/* Form attachments (the ones that are shown while creating and editing a post) */ - -.form-attachments { - display: flex; - flex-wrap: wrap; - color: gray; - font-size: .75em; - margin: 0 .3em; -} - -.form-attachment { - border: 2px solid black; - border-radius: .6em; - margin-top: .2em; - margin-right: .2em; - padding: .2em; - display: flex; - align-items: center; -} - -.form-attachment:last-child { - margin-right: 0; -} - -.remove-form-attachment { - font-size: .9em; - color: var(--failure); - background-color: white; - border-radius: .2em; - margin: 0 .2em; - padding: .2em; -} - -.remove-form-attachment:hover { - color: white; - background-color: var(--failure); - cursor: pointer; -} - diff --git a/src/DevHive.Angular/src/theme.scss b/src/DevHive.Angular/src/theme.scss deleted file mode 100644 index a87ae45..0000000 --- a/src/DevHive.Angular/src/theme.scss +++ /dev/null @@ -1,14 +0,0 @@ -// Import theming functions -@import '~@angular/material/theming'; -@import './styles.css'; -@include mat-core(); - -// Custom Angular theme - -// $my-custom-primary: mat-palette($mat-deep-purple); -// $my-custom-accent: mat-palette($mat-pink, 100, 500, A100); -// $my-custom-warn: mat-palette($mat-lime); - -// $my-custom-theme: mat-light-theme($my-custom-primary, $my-custom-accent, $my-custom-warn); - -// @include angular-material-theme($my-custom-theme);
\ No newline at end of file diff --git a/src/DevHive.Angular/tsconfig.app.json b/src/DevHive.Angular/tsconfig.app.json deleted file mode 100644 index 82d91dc..0000000 --- a/src/DevHive.Angular/tsconfig.app.json +++ /dev/null @@ -1,15 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./out-tsc/app", - "types": [] - }, - "files": [ - "src/main.ts", - "src/polyfills.ts" - ], - "include": [ - "src/**/*.d.ts" - ] -} diff --git a/src/DevHive.Angular/tsconfig.json b/src/DevHive.Angular/tsconfig.json deleted file mode 100644 index 9c7dbe4..0000000 --- a/src/DevHive.Angular/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "compileOnSave": false, - "compilerOptions": { - "baseUrl": "./", - "outDir": "./dist/out-tsc", - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "sourceMap": true, - "declaration": false, - "downlevelIteration": true, - "experimentalDecorators": true, - "moduleResolution": "node", - "importHelpers": true, - "target": "es2015", - "module": "es2020", - "lib": [ - "es2018", - "dom" - ], - "strictPropertyInitialization": false - }, - "angularCompilerOptions": { - "strictInjectionParameters": true, - "strictInputAccessModifiers": true, - "strictTemplates": true - } -} diff --git a/src/DevHive.Angular/tsconfig.spec.json b/src/DevHive.Angular/tsconfig.spec.json deleted file mode 100644 index 092345b..0000000 --- a/src/DevHive.Angular/tsconfig.spec.json +++ /dev/null @@ -1,18 +0,0 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./out-tsc/spec", - "types": [ - "jasmine" - ] - }, - "files": [ - "src/test.ts", - "src/polyfills.ts" - ], - "include": [ - "src/**/*.spec.ts", - "src/**/*.d.ts" - ] -} diff --git a/src/DevHive.Angular/tslint.json b/src/DevHive.Angular/tslint.json deleted file mode 100644 index 277c8eb..0000000 --- a/src/DevHive.Angular/tslint.json +++ /dev/null @@ -1,152 +0,0 @@ -{ - "extends": "tslint:recommended", - "rulesDirectory": [ - "codelyzer" - ], - "rules": { - "align": { - "options": [ - "parameters", - "statements" - ] - }, - "array-type": false, - "arrow-return-shorthand": true, - "curly": true, - "deprecation": { - "severity": "warning" - }, - "eofline": true, - "import-blacklist": [ - true, - "rxjs/Rx" - ], - "import-spacing": true, - "indent": { - "options": [ - "spaces" - ] - }, - "max-classes-per-file": false, - "max-line-length": [ - true, - 140 - ], - "member-ordering": [ - true, - { - "order": [ - "static-field", - "instance-field", - "static-method", - "instance-method" - ] - } - ], - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-empty": false, - "no-inferrable-types": [ - true, - "ignore-params" - ], - "no-non-null-assertion": true, - "no-redundant-jsdoc": true, - "no-switch-case-fall-through": true, - "no-var-requires": false, - "object-literal-key-quotes": [ - true, - "as-needed" - ], - "quotemark": [ - true, - "single" - ], - "semicolon": { - "options": [ - "always" - ] - }, - "space-before-function-paren": { - "options": { - "anonymous": "never", - "asyncArrow": "always", - "constructor": "never", - "method": "never", - "named": "never" - } - }, - "typedef": [ - true, - "call-signature" - ], - "typedef-whitespace": { - "options": [ - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - }, - { - "call-signature": "onespace", - "index-signature": "onespace", - "parameter": "onespace", - "property-declaration": "onespace", - "variable-declaration": "onespace" - } - ] - }, - "variable-name": { - "options": [ - "ban-keywords", - "check-format", - "allow-pascal-case" - ] - }, - "whitespace": { - "options": [ - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type", - "check-typecast" - ] - }, - "component-class-suffix": true, - "contextual-lifecycle": true, - "directive-class-suffix": true, - "no-conflicting-lifecycle": true, - "no-host-metadata-property": true, - "no-input-rename": true, - "no-inputs-metadata-property": true, - "no-output-native": true, - "no-output-on-prefix": true, - "no-output-rename": true, - "no-outputs-metadata-property": true, - "template-banana-in-box": true, - "template-no-negated-async": true, - "use-lifecycle-interface": true, - "use-pipe-transform-interface": true, - "directive-selector": [ - true, - "attribute", - "app", - "camelCase" - ], - "component-selector": [ - true, - "element", - "app", - "kebab-case" - ] - } -} diff --git a/src/DevHive.Common/Models/Misc/PasswordModifications.cs b/src/DevHive.Common/Models/Misc/PasswordModifications.cs deleted file mode 100644 index f10a334..0000000 --- a/src/DevHive.Common/Models/Misc/PasswordModifications.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Security.Cryptography; -using System.Text; - -namespace DevHive.Common.Models.Misc -{ - public static class PasswordModifications - { - public static string GeneratePasswordHash(string password) - { - return string.Join(string.Empty, SHA512.HashData(Encoding.ASCII.GetBytes(password))); - } - } -} diff --git a/src/DevHive.Data/ConnectionString.json b/src/DevHive.Data/ConnectionString.json deleted file mode 100644 index c8300b2..0000000 --- a/src/DevHive.Data/ConnectionString.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ConnectionStrings": { - "DEV": "Server=localhost;Port=5432;Database=API;User Id=postgres;Password=password;" - } -}
\ No newline at end of file diff --git a/src/DevHive.Data/Interfaces/Models/IComment.cs b/src/DevHive.Data/Interfaces/Models/IComment.cs deleted file mode 100644 index 97c1578..0000000 --- a/src/DevHive.Data/Interfaces/Models/IComment.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using DevHive.Data.Models; - -namespace DevHive.Data.Interfaces.Models -{ - public interface IComment : IModel - { - Post Post { get; set; } - - User Creator { get; set; } - - string Message { get; set; } - - DateTime TimeCreated { get; set; } - } -} diff --git a/src/DevHive.Data/Interfaces/Models/ILanguage.cs b/src/DevHive.Data/Interfaces/Models/ILanguage.cs deleted file mode 100644 index 9eed09d..0000000 --- a/src/DevHive.Data/Interfaces/Models/ILanguage.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using DevHive.Data.Models; - -namespace DevHive.Data.Interfaces.Models -{ - public interface ILanguage : IModel - { - string Name { get; set; } - HashSet<User> Users { get; set; } - } -} diff --git a/src/DevHive.Data/Interfaces/Models/IModel.cs b/src/DevHive.Data/Interfaces/Models/IModel.cs deleted file mode 100644 index f903af3..0000000 --- a/src/DevHive.Data/Interfaces/Models/IModel.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace DevHive.Data.Interfaces.Models -{ - public interface IModel - { - Guid Id { get; set; } - } -} diff --git a/src/DevHive.Data/Interfaces/Models/IPost.cs b/src/DevHive.Data/Interfaces/Models/IPost.cs deleted file mode 100644 index 712d955..0000000 --- a/src/DevHive.Data/Interfaces/Models/IPost.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using DevHive.Data.Models; -using DevHive.Data.RelationModels; - -namespace DevHive.Data.Interfaces.Models -{ - public interface IPost : IModel - { - User Creator { get; set; } - - string Message { get; set; } - - DateTime TimeCreated { get; set; } - - List<Comment> Comments { get; set; } - - // Rating Rating { get; set; } - - List<PostAttachments> Attachments { get; set; } - } -} diff --git a/src/DevHive.Data/Interfaces/Models/IProfilePicture.cs b/src/DevHive.Data/Interfaces/Models/IProfilePicture.cs deleted file mode 100644 index c3fcbea..0000000 --- a/src/DevHive.Data/Interfaces/Models/IProfilePicture.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using DevHive.Data.Models; - -namespace DevHive.Data.Interfaces.Models -{ - public interface IProfilePicture : IModel - { - Guid UserId { get; set; } - User User { get; set; } - - string PictureURL { get; set; } - } -} diff --git a/src/DevHive.Data/Interfaces/Models/IRating.cs b/src/DevHive.Data/Interfaces/Models/IRating.cs deleted file mode 100644 index d1b968f..0000000 --- a/src/DevHive.Data/Interfaces/Models/IRating.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using DevHive.Data.Models; - -namespace DevHive.Data.Interfaces.Models -{ - public interface IRating : IModel - { - // Post Post { get; set; } - - int Rate { get; set; } - - // HashSet<User> UsersThatRated { get; set; } - } -} diff --git a/src/DevHive.Data/Interfaces/Models/IRole.cs b/src/DevHive.Data/Interfaces/Models/IRole.cs deleted file mode 100644 index c8b7068..0000000 --- a/src/DevHive.Data/Interfaces/Models/IRole.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using DevHive.Data.Models; - -namespace DevHive.Data.Interfaces.Models -{ - public interface IRole - { - HashSet<User> Users { get; set; } - } -} diff --git a/src/DevHive.Data/Interfaces/Models/ITechnology.cs b/src/DevHive.Data/Interfaces/Models/ITechnology.cs deleted file mode 100644 index 153f75f..0000000 --- a/src/DevHive.Data/Interfaces/Models/ITechnology.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using DevHive.Data.Models; - -namespace DevHive.Data.Interfaces.Models -{ - public interface ITechnology : IModel - { - string Name { get; set; } - HashSet<User> Users { get; set; } - } -} diff --git a/src/DevHive.Data/Interfaces/Models/IUser.cs b/src/DevHive.Data/Interfaces/Models/IUser.cs deleted file mode 100644 index fcd741c..0000000 --- a/src/DevHive.Data/Interfaces/Models/IUser.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using DevHive.Data.Models; -using DevHive.Data.RelationModels; - -namespace DevHive.Data.Interfaces.Models -{ - public interface IUser : IModel - { - string FirstName { get; set; } - - string LastName { get; set; } - - ProfilePicture ProfilePicture { get; set; } - - HashSet<Language> Languages { get; set; } - - HashSet<Technology> Technologies { get; set; } - - HashSet<Role> Roles { get; set; } - } -} diff --git a/src/DevHive.Data/Interfaces/Repositories/IRatingRepository.cs b/src/DevHive.Data/Interfaces/Repositories/IRatingRepository.cs deleted file mode 100644 index f77f301..0000000 --- a/src/DevHive.Data/Interfaces/Repositories/IRatingRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Threading.Tasks; -using DevHive.Data.Models; -using DevHive.Data.Repositories.Interfaces; - -namespace DevHive.Data.Interfaces.Repositories -{ - public interface IRatingRepository : IRepository<Rating> - { - Task<Rating> GetRatingByPostId(Guid postId); - Task<bool> UserRatedPost(Guid userId, Guid postId); - } -} diff --git a/src/DevHive.Data/Migrations/20210205140955_rating.Designer.cs b/src/DevHive.Data/Migrations/20210205140955_rating.Designer.cs deleted file mode 100644 index 8d2adc4..0000000 --- a/src/DevHive.Data/Migrations/20210205140955_rating.Designer.cs +++ /dev/null @@ -1,665 +0,0 @@ -// <auto-generated /> -using System; -using System.Collections.Generic; -using DevHive.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -namespace DevHive.Data.Migrations -{ - [DbContext(typeof(DevHiveContext))] - [Migration("20210205140955_rating")] - partial class rating - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .UseIdentityByDefaultColumns() - .HasAnnotation("Relational:MaxIdentifierLength", 63) - .HasAnnotation("ProductVersion", "5.0.1"); - - modelBuilder.Entity("DevHive.Data.Models.Comment", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<Guid?>("CreatorId") - .HasColumnType("uuid"); - - b.Property<string>("Message") - .HasColumnType("text"); - - b.Property<Guid?>("PostId") - .HasColumnType("uuid"); - - b.Property<DateTime>("TimeCreated") - .HasColumnType("timestamp without time zone"); - - b.HasKey("Id"); - - b.HasIndex("CreatorId"); - - b.HasIndex("PostId"); - - b.ToTable("Comments"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Language", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("Name") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Languages"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Post", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<Guid?>("CreatorId") - .HasColumnType("uuid"); - - b.Property<List<string>>("FileUrls") - .HasColumnType("text[]"); - - b.Property<string>("Message") - .HasColumnType("text"); - - b.Property<DateTime>("TimeCreated") - .HasColumnType("timestamp without time zone"); - - b.HasKey("Id"); - - b.HasIndex("CreatorId"); - - b.ToTable("Posts"); - }); - - modelBuilder.Entity("DevHive.Data.Models.ProfilePicture", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("PictureURL") - .HasColumnType("text"); - - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId") - .IsUnique(); - - b.ToTable("ProfilePicture"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Rating", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<Guid>("PostId") - .HasColumnType("uuid"); - - b.Property<int>("Rate") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("PostId") - .IsUnique(); - - b.ToTable("Rating"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Role", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property<string>("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<string>("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Technology", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("Name") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Technologies"); - }); - - modelBuilder.Entity("DevHive.Data.Models.User", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<int>("AccessFailedCount") - .HasColumnType("integer"); - - b.Property<string>("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property<string>("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<bool>("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property<string>("FirstName") - .HasColumnType("text"); - - b.Property<string>("LastName") - .HasColumnType("text"); - - b.Property<bool>("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property<DateTimeOffset?>("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property<string>("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<string>("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<string>("PasswordHash") - .HasColumnType("text"); - - b.Property<string>("PhoneNumber") - .HasColumnType("text"); - - b.Property<bool>("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property<string>("SecurityStamp") - .HasColumnType("text"); - - b.Property<bool>("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property<string>("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("UserName") - .IsUnique(); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.RatedPost", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("PostId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "PostId"); - - b.HasIndex("PostId"); - - b.ToTable("RatedPosts"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserFriend", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("FriendId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "FriendId"); - - b.HasIndex("FriendId"); - - b.ToTable("UserFriends"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserRate", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<bool>("Liked") - .HasColumnType("boolean"); - - b.Property<Guid?>("PostId") - .HasColumnType("uuid"); - - b.Property<Guid?>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("PostId"); - - b.HasIndex("UserId"); - - b.ToTable("UserRates"); - }); - - modelBuilder.Entity("LanguageUser", b => - { - b.Property<Guid>("LanguagesId") - .HasColumnType("uuid"); - - b.Property<Guid>("UsersId") - .HasColumnType("uuid"); - - b.HasKey("LanguagesId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("LanguageUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .UseIdentityByDefaultColumn(); - - b.Property<string>("ClaimType") - .HasColumnType("text"); - - b.Property<string>("ClaimValue") - .HasColumnType("text"); - - b.Property<Guid>("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .UseIdentityByDefaultColumn(); - - b.Property<string>("ClaimType") - .HasColumnType("text"); - - b.Property<string>("ClaimValue") - .HasColumnType("text"); - - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => - { - b.Property<string>("LoginProvider") - .HasColumnType("text"); - - b.Property<string>("ProviderKey") - .HasColumnType("text"); - - b.Property<string>("ProviderDisplayName") - .HasColumnType("text"); - - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("RoleId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<string>("LoginProvider") - .HasColumnType("text"); - - b.Property<string>("Name") - .HasColumnType("text"); - - b.Property<string>("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("RoleUser", b => - { - b.Property<Guid>("RolesId") - .HasColumnType("uuid"); - - b.Property<Guid>("UsersId") - .HasColumnType("uuid"); - - b.HasKey("RolesId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("RoleUser"); - }); - - modelBuilder.Entity("TechnologyUser", b => - { - b.Property<Guid>("TechnologiesId") - .HasColumnType("uuid"); - - b.Property<Guid>("UsersId") - .HasColumnType("uuid"); - - b.HasKey("TechnologiesId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("TechnologyUser"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Comment", b => - { - b.HasOne("DevHive.Data.Models.User", "Creator") - .WithMany("Comments") - .HasForeignKey("CreatorId"); - - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany("Comments") - .HasForeignKey("PostId"); - - b.Navigation("Creator"); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Post", b => - { - b.HasOne("DevHive.Data.Models.User", "Creator") - .WithMany("Posts") - .HasForeignKey("CreatorId"); - - b.Navigation("Creator"); - }); - - modelBuilder.Entity("DevHive.Data.Models.ProfilePicture", b => - { - b.HasOne("DevHive.Data.Models.User", "User") - .WithOne("ProfilePicture") - .HasForeignKey("DevHive.Data.Models.ProfilePicture", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Rating", b => - { - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithOne("Rating") - .HasForeignKey("DevHive.Data.Models.Rating", "PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.RatedPost", b => - { - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany() - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany("RatedPosts") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserFriend", b => - { - b.HasOne("DevHive.Data.Models.User", "Friend") - .WithMany("FriendsOf") - .HasForeignKey("FriendId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany("MyFriends") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Friend"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserRate", b => - { - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany() - .HasForeignKey("PostId"); - - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Post"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LanguageUser", b => - { - b.HasOne("DevHive.Data.Models.Language", null) - .WithMany() - .HasForeignKey("LanguagesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.Role", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.Role", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("RoleUser", b => - { - b.HasOne("DevHive.Data.Models.Role", null) - .WithMany() - .HasForeignKey("RolesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("TechnologyUser", b => - { - b.HasOne("DevHive.Data.Models.Technology", null) - .WithMany() - .HasForeignKey("TechnologiesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("DevHive.Data.Models.Post", b => - { - b.Navigation("Comments"); - - b.Navigation("Rating"); - }); - - modelBuilder.Entity("DevHive.Data.Models.User", b => - { - b.Navigation("Comments"); - - b.Navigation("FriendsOf"); - - b.Navigation("MyFriends"); - - b.Navigation("Posts"); - - b.Navigation("ProfilePicture"); - - b.Navigation("RatedPosts"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/DevHive.Data/Migrations/20210205150447_Friends_Init_Config.Designer.cs b/src/DevHive.Data/Migrations/20210205150447_Friends_Init_Config.Designer.cs deleted file mode 100644 index c11374a..0000000 --- a/src/DevHive.Data/Migrations/20210205150447_Friends_Init_Config.Designer.cs +++ /dev/null @@ -1,643 +0,0 @@ -// <auto-generated /> -using System; -using System.Collections.Generic; -using DevHive.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -namespace DevHive.Data.Migrations -{ - [DbContext(typeof(DevHiveContext))] - [Migration("20210205150447_Friends_Init_Config")] - partial class Friends_Init_Config - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .UseIdentityByDefaultColumns() - .HasAnnotation("Relational:MaxIdentifierLength", 63) - .HasAnnotation("ProductVersion", "5.0.1"); - - modelBuilder.Entity("DevHive.Data.Models.Comment", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<Guid?>("CreatorId") - .HasColumnType("uuid"); - - b.Property<string>("Message") - .HasColumnType("text"); - - b.Property<Guid?>("PostId") - .HasColumnType("uuid"); - - b.Property<DateTime>("TimeCreated") - .HasColumnType("timestamp without time zone"); - - b.HasKey("Id"); - - b.HasIndex("CreatorId"); - - b.HasIndex("PostId"); - - b.ToTable("Comments"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Language", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("Name") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Languages"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Post", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<Guid?>("CreatorId") - .HasColumnType("uuid"); - - b.Property<List<string>>("FileUrls") - .HasColumnType("text[]"); - - b.Property<string>("Message") - .HasColumnType("text"); - - b.Property<DateTime>("TimeCreated") - .HasColumnType("timestamp without time zone"); - - b.HasKey("Id"); - - b.HasIndex("CreatorId"); - - b.ToTable("Posts"); - }); - - modelBuilder.Entity("DevHive.Data.Models.ProfilePicture", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("PictureURL") - .HasColumnType("text"); - - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId") - .IsUnique(); - - b.ToTable("ProfilePicture"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Rating", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<int>("Rate") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("Rating"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Role", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property<string>("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<string>("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Technology", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("Name") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Technologies"); - }); - - modelBuilder.Entity("DevHive.Data.Models.User", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<int>("AccessFailedCount") - .HasColumnType("integer"); - - b.Property<string>("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property<string>("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<bool>("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property<string>("FirstName") - .HasColumnType("text"); - - b.Property<string>("LastName") - .HasColumnType("text"); - - b.Property<bool>("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property<DateTimeOffset?>("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property<string>("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<string>("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<string>("PasswordHash") - .HasColumnType("text"); - - b.Property<string>("PhoneNumber") - .HasColumnType("text"); - - b.Property<bool>("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property<string>("SecurityStamp") - .HasColumnType("text"); - - b.Property<bool>("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property<Guid?>("UserId") - .HasColumnType("uuid"); - - b.Property<string>("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("UserId"); - - b.HasIndex("UserName") - .IsUnique(); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.RatedPost", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("PostId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "PostId"); - - b.HasIndex("PostId"); - - b.ToTable("RatedPosts"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserFriend", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("FriendId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "FriendId"); - - b.HasIndex("FriendId"); - - b.ToTable("UserFriends"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserRate", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<bool>("Rate") - .HasColumnType("boolean"); - - b.Property<Guid?>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("UserRates"); - }); - - modelBuilder.Entity("LanguageUser", b => - { - b.Property<Guid>("LanguagesId") - .HasColumnType("uuid"); - - b.Property<Guid>("UsersId") - .HasColumnType("uuid"); - - b.HasKey("LanguagesId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("LanguageUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .UseIdentityByDefaultColumn(); - - b.Property<string>("ClaimType") - .HasColumnType("text"); - - b.Property<string>("ClaimValue") - .HasColumnType("text"); - - b.Property<Guid>("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .UseIdentityByDefaultColumn(); - - b.Property<string>("ClaimType") - .HasColumnType("text"); - - b.Property<string>("ClaimValue") - .HasColumnType("text"); - - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => - { - b.Property<string>("LoginProvider") - .HasColumnType("text"); - - b.Property<string>("ProviderKey") - .HasColumnType("text"); - - b.Property<string>("ProviderDisplayName") - .HasColumnType("text"); - - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("RoleId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<string>("LoginProvider") - .HasColumnType("text"); - - b.Property<string>("Name") - .HasColumnType("text"); - - b.Property<string>("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("RoleUser", b => - { - b.Property<Guid>("RolesId") - .HasColumnType("uuid"); - - b.Property<Guid>("UsersId") - .HasColumnType("uuid"); - - b.HasKey("RolesId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("RoleUser"); - }); - - modelBuilder.Entity("TechnologyUser", b => - { - b.Property<Guid>("TechnologiesId") - .HasColumnType("uuid"); - - b.Property<Guid>("UsersId") - .HasColumnType("uuid"); - - b.HasKey("TechnologiesId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("TechnologyUser"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Comment", b => - { - b.HasOne("DevHive.Data.Models.User", "Creator") - .WithMany("Comments") - .HasForeignKey("CreatorId"); - - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany("Comments") - .HasForeignKey("PostId"); - - b.Navigation("Creator"); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Post", b => - { - b.HasOne("DevHive.Data.Models.User", "Creator") - .WithMany("Posts") - .HasForeignKey("CreatorId"); - - b.Navigation("Creator"); - }); - - modelBuilder.Entity("DevHive.Data.Models.ProfilePicture", b => - { - b.HasOne("DevHive.Data.Models.User", "User") - .WithOne("ProfilePicture") - .HasForeignKey("DevHive.Data.Models.ProfilePicture", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("DevHive.Data.Models.User", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany("Friends") - .HasForeignKey("UserId"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.RatedPost", b => - { - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany() - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserFriend", b => - { - b.HasOne("DevHive.Data.Models.User", "Friend") - .WithMany() - .HasForeignKey("FriendId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Friend"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserRate", b => - { - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LanguageUser", b => - { - b.HasOne("DevHive.Data.Models.Language", null) - .WithMany() - .HasForeignKey("LanguagesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.Role", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.Role", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("RoleUser", b => - { - b.HasOne("DevHive.Data.Models.Role", null) - .WithMany() - .HasForeignKey("RolesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("TechnologyUser", b => - { - b.HasOne("DevHive.Data.Models.Technology", null) - .WithMany() - .HasForeignKey("TechnologiesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("DevHive.Data.Models.Post", b => - { - b.Navigation("Comments"); - }); - - modelBuilder.Entity("DevHive.Data.Models.User", b => - { - b.Navigation("Comments"); - - b.Navigation("Friends"); - - b.Navigation("Posts"); - - b.Navigation("ProfilePicture"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/DevHive.Data/Migrations/20210205150447_Friends_Init_Config.cs b/src/DevHive.Data/Migrations/20210205150447_Friends_Init_Config.cs deleted file mode 100644 index 1fc970d..0000000 --- a/src/DevHive.Data/Migrations/20210205150447_Friends_Init_Config.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace DevHive.Data.Migrations -{ - public partial class Friends_Init_Config : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn<Guid>( - name: "UserId", - table: "AspNetUsers", - type: "uuid", - nullable: true); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUsers_UserId", - table: "AspNetUsers", - column: "UserId"); - - migrationBuilder.AddForeignKey( - name: "FK_AspNetUsers_AspNetUsers_UserId", - table: "AspNetUsers", - column: "UserId", - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_AspNetUsers_AspNetUsers_UserId", - table: "AspNetUsers"); - - migrationBuilder.DropIndex( - name: "IX_AspNetUsers_UserId", - table: "AspNetUsers"); - - migrationBuilder.DropColumn( - name: "UserId", - table: "AspNetUsers"); - } - } -} diff --git a/src/DevHive.Data/Migrations/20210205154810_PostFileAttachments.Designer.cs b/src/DevHive.Data/Migrations/20210205154810_PostFileAttachments.Designer.cs deleted file mode 100644 index 5d8e13a..0000000 --- a/src/DevHive.Data/Migrations/20210205154810_PostFileAttachments.Designer.cs +++ /dev/null @@ -1,691 +0,0 @@ -// <auto-generated /> -using System; -using DevHive.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -namespace DevHive.Data.Migrations -{ - [DbContext(typeof(DevHiveContext))] - [Migration("20210205154810_PostFileAttachments")] - partial class PostFileAttachments - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .UseIdentityByDefaultColumns() - .HasAnnotation("Relational:MaxIdentifierLength", 63) - .HasAnnotation("ProductVersion", "5.0.1"); - - modelBuilder.Entity("DevHive.Data.Models.Comment", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<Guid?>("CreatorId") - .HasColumnType("uuid"); - - b.Property<string>("Message") - .HasColumnType("text"); - - b.Property<Guid?>("PostId") - .HasColumnType("uuid"); - - b.Property<DateTime>("TimeCreated") - .HasColumnType("timestamp without time zone"); - - b.HasKey("Id"); - - b.HasIndex("CreatorId"); - - b.HasIndex("PostId"); - - b.ToTable("Comments"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Language", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("Name") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Languages"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Post", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<Guid?>("CreatorId") - .HasColumnType("uuid"); - - b.Property<string>("Message") - .HasColumnType("text"); - - b.Property<DateTime>("TimeCreated") - .HasColumnType("timestamp without time zone"); - - b.HasKey("Id"); - - b.HasIndex("CreatorId"); - - b.ToTable("Posts"); - }); - - modelBuilder.Entity("DevHive.Data.Models.ProfilePicture", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("PictureURL") - .HasColumnType("text"); - - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId") - .IsUnique(); - - b.ToTable("ProfilePicture"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Rating", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<Guid>("PostId") - .HasColumnType("uuid"); - - b.Property<int>("Rate") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("PostId") - .IsUnique(); - - b.ToTable("Rating"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Role", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property<string>("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<string>("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Technology", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("Name") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Technologies"); - }); - - modelBuilder.Entity("DevHive.Data.Models.User", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<int>("AccessFailedCount") - .HasColumnType("integer"); - - b.Property<string>("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property<string>("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<bool>("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property<string>("FirstName") - .HasColumnType("text"); - - b.Property<string>("LastName") - .HasColumnType("text"); - - b.Property<bool>("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property<DateTimeOffset?>("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property<string>("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<string>("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property<string>("PasswordHash") - .HasColumnType("text"); - - b.Property<string>("PhoneNumber") - .HasColumnType("text"); - - b.Property<bool>("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property<string>("SecurityStamp") - .HasColumnType("text"); - - b.Property<bool>("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property<string>("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("UserName") - .IsUnique(); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.PostAttachments", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<string>("FileUrl") - .HasColumnType("text"); - - b.Property<Guid?>("PostId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("PostId"); - - b.ToTable("PostAttachments"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.RatedPost", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("PostId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "PostId"); - - b.HasIndex("PostId"); - - b.ToTable("RatedPosts"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserFriend", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("FriendId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "FriendId"); - - b.HasIndex("FriendId"); - - b.ToTable("UserFriends"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserRate", b => - { - b.Property<Guid>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property<bool>("Liked") - .HasColumnType("boolean"); - - b.Property<Guid?>("PostId") - .HasColumnType("uuid"); - - b.Property<Guid?>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("PostId"); - - b.HasIndex("UserId"); - - b.ToTable("UserRates"); - }); - - modelBuilder.Entity("LanguageUser", b => - { - b.Property<Guid>("LanguagesId") - .HasColumnType("uuid"); - - b.Property<Guid>("UsersId") - .HasColumnType("uuid"); - - b.HasKey("LanguagesId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("LanguageUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .UseIdentityByDefaultColumn(); - - b.Property<string>("ClaimType") - .HasColumnType("text"); - - b.Property<string>("ClaimValue") - .HasColumnType("text"); - - b.Property<Guid>("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => - { - b.Property<int>("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .UseIdentityByDefaultColumn(); - - b.Property<string>("ClaimType") - .HasColumnType("text"); - - b.Property<string>("ClaimValue") - .HasColumnType("text"); - - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => - { - b.Property<string>("LoginProvider") - .HasColumnType("text"); - - b.Property<string>("ProviderKey") - .HasColumnType("text"); - - b.Property<string>("ProviderDisplayName") - .HasColumnType("text"); - - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<Guid>("RoleId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => - { - b.Property<Guid>("UserId") - .HasColumnType("uuid"); - - b.Property<string>("LoginProvider") - .HasColumnType("text"); - - b.Property<string>("Name") - .HasColumnType("text"); - - b.Property<string>("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("RoleUser", b => - { - b.Property<Guid>("RolesId") - .HasColumnType("uuid"); - - b.Property<Guid>("UsersId") - .HasColumnType("uuid"); - - b.HasKey("RolesId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("RoleUser"); - }); - - modelBuilder.Entity("TechnologyUser", b => - { - b.Property<Guid>("TechnologiesId") - .HasColumnType("uuid"); - - b.Property<Guid>("UsersId") - .HasColumnType("uuid"); - - b.HasKey("TechnologiesId", "UsersId"); - - b.HasIndex("UsersId"); - - b.ToTable("TechnologyUser"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Comment", b => - { - b.HasOne("DevHive.Data.Models.User", "Creator") - .WithMany("Comments") - .HasForeignKey("CreatorId"); - - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany("Comments") - .HasForeignKey("PostId"); - - b.Navigation("Creator"); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Post", b => - { - b.HasOne("DevHive.Data.Models.User", "Creator") - .WithMany("Posts") - .HasForeignKey("CreatorId"); - - b.Navigation("Creator"); - }); - - modelBuilder.Entity("DevHive.Data.Models.ProfilePicture", b => - { - b.HasOne("DevHive.Data.Models.User", "User") - .WithOne("ProfilePicture") - .HasForeignKey("DevHive.Data.Models.ProfilePicture", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("DevHive.Data.Models.Rating", b => - { - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithOne("Rating") - .HasForeignKey("DevHive.Data.Models.Rating", "PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.PostAttachments", b => - { - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany("Attachments") - .HasForeignKey("PostId"); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.RatedPost", b => - { - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany() - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany("RatedPosts") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserFriend", b => - { - b.HasOne("DevHive.Data.Models.User", "Friend") - .WithMany("FriendsOf") - .HasForeignKey("FriendId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany("MyFriends") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Friend"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("DevHive.Data.RelationModels.UserRate", b => - { - b.HasOne("DevHive.Data.Models.Post", "Post") - .WithMany() - .HasForeignKey("PostId"); - - b.HasOne("DevHive.Data.Models.User", "User") - .WithMany() - .HasForeignKey("UserId"); - - b.Navigation("Post"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LanguageUser", b => - { - b.HasOne("DevHive.Data.Models.Language", null) - .WithMany() - .HasForeignKey("LanguagesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.Role", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.Role", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b => - { - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("RoleUser", b => - { - b.HasOne("DevHive.Data.Models.Role", null) - .WithMany() - .HasForeignKey("RolesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("TechnologyUser", b => - { - b.HasOne("DevHive.Data.Models.Technology", null) - .WithMany() - .HasForeignKey("TechnologiesId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("DevHive.Data.Models.User", null) - .WithMany() - .HasForeignKey("UsersId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("DevHive.Data.Models.Post", b => - { - b.Navigation("Attachments"); - - b.Navigation("Comments"); - - b.Navigation("Rating"); - }); - - modelBuilder.Entity("DevHive.Data.Models.User", b => - { - b.Navigation("Comments"); - - b.Navigation("FriendsOf"); - - b.Navigation("MyFriends"); - - b.Navigation("Posts"); - - b.Navigation("ProfilePicture"); - - b.Navigation("RatedPosts"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/DevHive.Data/Migrations/20210205154810_PostFileAttachments.cs b/src/DevHive.Data/Migrations/20210205154810_PostFileAttachments.cs deleted file mode 100644 index 7b7b933..0000000 --- a/src/DevHive.Data/Migrations/20210205154810_PostFileAttachments.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace DevHive.Data.Migrations -{ - public partial class PostFileAttachments : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "FileUrls", - table: "Posts"); - - migrationBuilder.CreateTable( - name: "PostAttachments", - columns: table => new - { - Id = table.Column<Guid>(type: "uuid", nullable: false), - PostId = table.Column<Guid>(type: "uuid", nullable: true), - FileUrl = table.Column<string>(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_PostAttachments", x => x.Id); - table.ForeignKey( - name: "FK_PostAttachments_Posts_PostId", - column: x => x.PostId, - principalTable: "Posts", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateIndex( - name: "IX_PostAttachments_PostId", - table: "PostAttachments", - column: "PostId"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "PostAttachments"); - - migrationBuilder.AddColumn<string[]>( - name: "FileUrls", - table: "Posts", - type: "text[]", - nullable: true); - } - } -} diff --git a/src/DevHive.Data/Migrations/20210205160520_Friends_First_Tweek.cs b/src/DevHive.Data/Migrations/20210205160520_Friends_First_Tweek.cs deleted file mode 100644 index 565f863..0000000 --- a/src/DevHive.Data/Migrations/20210205160520_Friends_First_Tweek.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace DevHive.Data.Migrations -{ - public partial class Friends_First_Tweek : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "UserFriends"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "UserFriends", - columns: table => new - { - UserId = table.Column<Guid>(type: "uuid", nullable: false), - FriendId = table.Column<Guid>(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_UserFriends", x => new { x.UserId, x.FriendId }); - table.ForeignKey( - name: "FK_UserFriends_AspNetUsers_FriendId", - column: x => x.FriendId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_UserFriends_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_UserFriends_FriendId", - table: "UserFriends", - column: "FriendId"); - } - } -} diff --git a/src/DevHive.Data/Models/Rating.cs b/src/DevHive.Data/Models/Rating.cs deleted file mode 100644 index e1bedd2..0000000 --- a/src/DevHive.Data/Models/Rating.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using DevHive.Data.Interfaces.Models; - -namespace DevHive.Data.Models -{ - public class Rating : IRating - { - public Guid Id { get; set; } - - public Guid PostId { get; set; } - - public Post Post { get; set; } - - public int Rate { get; set; } - } -} diff --git a/src/DevHive.Data/RelationModels/RatedPost.cs b/src/DevHive.Data/RelationModels/RatedPost.cs deleted file mode 100644 index 7001d92..0000000 --- a/src/DevHive.Data/RelationModels/RatedPost.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations.Schema; -using System.Reflection.Metadata.Ecma335; -using DevHive.Data.Models; -using Microsoft.EntityFrameworkCore; - -namespace DevHive.Data.RelationModels -{ - [Table("RatedPosts")] - public class RatedPost - { - public Guid UserId { get; set; } - public User User { get; set; } - - public Guid PostId { get; set; } - public Post Post { get; set; } - } -} diff --git a/src/DevHive.Data/RelationModels/UserRate.cs b/src/DevHive.Data/RelationModels/UserRate.cs deleted file mode 100644 index 696e6f3..0000000 --- a/src/DevHive.Data/RelationModels/UserRate.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations.Schema; -using DevHive.Data.Models; - -namespace DevHive.Data.RelationModels -{ - [Table("UserRates")] - public class UserRate - { - public Guid Id { get; set; } - - public User User { get; set; } - - public bool Liked { get; set; } - - public Post Post { get; set; } - } -} diff --git a/src/DevHive.Data/Repositories/RatingRepository.cs b/src/DevHive.Data/Repositories/RatingRepository.cs deleted file mode 100644 index 1be8fe8..0000000 --- a/src/DevHive.Data/Repositories/RatingRepository.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using DevHive.Data.Interfaces.Repositories; -using DevHive.Data.Models; -using Microsoft.EntityFrameworkCore; - -namespace DevHive.Data.Repositories -{ - public class RatingRepository : BaseRepository<Rating>, IRatingRepository - { - private readonly DevHiveContext _context; - private readonly IPostRepository _postRepository; - - public RatingRepository(DevHiveContext context, IPostRepository postRepository) - : base(context) - { - this._context = context; - this._postRepository = postRepository; - } - - public async Task<Rating> GetRatingByPostId(Guid postId) - { - return await this._context.Rating - .FirstOrDefaultAsync(x => x.Post.Id == postId); - } - - public async Task<bool> UserRatedPost(Guid userId, Guid postId) - { - return await this._context.UserRate - .Where(x => x.Post.Id == postId) - .AnyAsync(x => x.User.Id == userId); - } - } -} diff --git a/src/DevHive.Data/Repositories/RoleRepository.cs b/src/DevHive.Data/Repositories/RoleRepository.cs deleted file mode 100644 index 441efef..0000000 --- a/src/DevHive.Data/Repositories/RoleRepository.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Threading.Tasks; -using DevHive.Data.Interfaces.Repositories; -using DevHive.Data.Models; -using Microsoft.EntityFrameworkCore; - -namespace DevHive.Data.Repositories -{ - public class RoleRepository : BaseRepository<Role>, IRoleRepository - { - private readonly DevHiveContext _context; - - public RoleRepository(DevHiveContext context) - : base(context) - { - this._context = context; - } - - #region Read - public async Task<Role> GetByNameAsync(string name) - { - return await this._context.Roles - .FirstOrDefaultAsync(x => x.Name == name); - } - #endregion - - public override async Task<bool> EditAsync(Guid id, Role newEntity) - { - Role role = await this.GetByIdAsync(id); - - this._context - .Entry(role) - .CurrentValues - .SetValues(newEntity); - - return await this.SaveChangesAsync(); - } - - #region Validations - public async Task<bool> DoesNameExist(string name) - { - return await this._context.Roles - .AsNoTracking() - .AnyAsync(r => r.Name == name); - } - - public async Task<bool> DoesRoleExist(Guid id) - { - return await this._context.Roles - .AsNoTracking() - .AnyAsync(r => r.Id == id); - } - #endregion - } -} diff --git a/src/DevHive.Data/Repositories/UserRepository.cs b/src/DevHive.Data/Repositories/UserRepository.cs deleted file mode 100644 index 6e97e60..0000000 --- a/src/DevHive.Data/Repositories/UserRepository.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; -using AutoMapper.Mappers; -using DevHive.Data.Interfaces.Repositories; -using DevHive.Data.Models; -using DevHive.Data.RelationModels; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; - -namespace DevHive.Data.Repositories -{ - public class UserRepository : BaseRepository<User>, IUserRepository - { - private readonly DevHiveContext _context; - - public UserRepository(DevHiveContext context) - : base(context) - { - this._context = context; - } - - #region Read - public override async Task<User> GetByIdAsync(Guid id) - { - return await this._context.Users - .Include(x => x.Roles) - .Include(x => x.Languages) - .Include(x => x.Technologies) - .Include(x => x.Posts) - .Include(x => x.Friends) - .Include(x => x.ProfilePicture) - .FirstOrDefaultAsync(x => x.Id == id); - } - - public async Task<User> GetByUsernameAsync(string username) - { - return await this._context.Users - .Include(x => x.Roles) - .Include(x => x.Languages) - .Include(x => x.Technologies) - .Include(x => x.Posts) - .Include(x => x.Friends) - .Include(x => x.ProfilePicture) - .FirstOrDefaultAsync(x => x.UserName == username); - } - #endregion - - #region Update - public async Task<bool> UpdateProfilePicture(Guid userId, string pictureUrl) - { - User user = await this.GetByIdAsync(userId); - - user.ProfilePicture.PictureURL = pictureUrl; - - return await this.SaveChangesAsync(); - } - #endregion - - #region Validations - public async Task<bool> DoesUserExistAsync(Guid id) - { - return await this._context.Users - .AsNoTracking() - .AnyAsync(x => x.Id == id); - } - - public async Task<bool> DoesUsernameExistAsync(string username) - { - return await this._context.Users - .AsNoTracking() - .AnyAsync(u => u.UserName == username); - } - - public async Task<bool> DoesEmailExistAsync(string email) - { - return await this._context.Users - .AsNoTracking() - .AnyAsync(u => u.Email == email); - } - - public async Task<bool> ValidateFriendsCollectionAsync(List<string> usernames) - { - bool valid = true; - - foreach (var username in usernames) - { - if (!await this._context.Users.AnyAsync(x => x.UserName == username)) - { - valid = false; - break; - } - } - return valid; - } - - public bool DoesUserHaveThisUsername(Guid id, string username) - { - return this._context.Users - .AsNoTracking() - .Any(x => x.Id == id && - x.UserName == username); - } - #endregion - } -} diff --git a/src/DevHive.Services/Configurations/Mapping/RatingMappings.cs b/src/DevHive.Services/Configurations/Mapping/RatingMappings.cs deleted file mode 100644 index 1dbb7b4..0000000 --- a/src/DevHive.Services/Configurations/Mapping/RatingMappings.cs +++ /dev/null @@ -1,15 +0,0 @@ -using AutoMapper; -using DevHive.Data.Models; -using DevHive.Services.Models.Post.Rating; - -namespace DevHive.Services.Configurations.Mapping -{ - public class RatingMappings : Profile - { - public RatingMappings() - { - // CreateMap<Rating, ReadPostRatingServiceModel>() - // .ForMember(dest => dest.PostId, src => src.MapFrom(p => p.Post.Id)); - } - } -} diff --git a/src/DevHive.Services/Interfaces/IRateService.cs b/src/DevHive.Services/Interfaces/IRateService.cs deleted file mode 100644 index 359ef55..0000000 --- a/src/DevHive.Services/Interfaces/IRateService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Threading.Tasks; -using DevHive.Data.Models; -using DevHive.Services.Models.Post.Rating; - -namespace DevHive.Services.Interfaces -{ - public interface IRateService - { - Task<ReadPostRatingServiceModel> RatePost(RatePostServiceModel ratePostServiceModel); - - bool HasUserRatedThisPost(User user, Post post); - } -} diff --git a/src/DevHive.Services/Interfaces/IUserService.cs b/src/DevHive.Services/Interfaces/IUserService.cs deleted file mode 100644 index 9e2b4e3..0000000 --- a/src/DevHive.Services/Interfaces/IUserService.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Threading.Tasks; -using DevHive.Common.Models.Identity; -using DevHive.Services.Models.Identity.User; - -namespace DevHive.Services.Interfaces -{ - public interface IUserService - { - Task<TokenModel> LoginUser(LoginServiceModel loginModel); - Task<TokenModel> RegisterUser(RegisterServiceModel registerModel); - - Task<UserServiceModel> GetUserByUsername(string username); - Task<UserServiceModel> GetUserById(Guid id); - - Task<UserServiceModel> UpdateUser(UpdateUserServiceModel updateModel); - Task<ProfilePictureServiceModel> UpdateProfilePicture(UpdateProfilePictureServiceModel updateProfilePictureServiceModel); - - Task<bool> DeleteUser(Guid id); - - Task<bool> ValidJWT(Guid id, string rawTokenData); - - Task<TokenModel> SuperSecretPromotionToAdmin(Guid userId); - } -} diff --git a/src/DevHive.Services/Models/Identity/User/ProfilePictureServiceModel.cs b/src/DevHive.Services/Models/Identity/User/ProfilePictureServiceModel.cs deleted file mode 100644 index ad81057..0000000 --- a/src/DevHive.Services/Models/Identity/User/ProfilePictureServiceModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DevHive.Services.Models.Identity.User -{ - public class ProfilePictureServiceModel - { - public string ProfilePictureURL { get; set; } - } -} diff --git a/src/DevHive.Services/Models/Identity/User/UpdateProfilePictureServiceModel.cs b/src/DevHive.Services/Models/Identity/User/UpdateProfilePictureServiceModel.cs deleted file mode 100644 index 8563953..0000000 --- a/src/DevHive.Services/Models/Identity/User/UpdateProfilePictureServiceModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using Microsoft.AspNetCore.Http; - -namespace DevHive.Services.Models.Identity.User -{ - public class UpdateProfilePictureServiceModel - { - public Guid UserId { get; set; } - - public IFormFile Picture { get; set; } - } -} diff --git a/src/DevHive.Services/Models/Post/Rating/RatePostServiceModel.cs b/src/DevHive.Services/Models/Post/Rating/RatePostServiceModel.cs deleted file mode 100644 index d4eb7bd..0000000 --- a/src/DevHive.Services/Models/Post/Rating/RatePostServiceModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace DevHive.Services.Models.Post.Rating -{ - public class RatePostServiceModel - { - public Guid UserId { get; set; } - - public Guid PostId { get; set; } - - public bool Liked { get; set; } - } -} diff --git a/src/DevHive.Services/Models/Post/Rating/ReadPostRatingServiceModel.cs b/src/DevHive.Services/Models/Post/Rating/ReadPostRatingServiceModel.cs deleted file mode 100644 index 8c73aaf..0000000 --- a/src/DevHive.Services/Models/Post/Rating/ReadPostRatingServiceModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace DevHive.Services.Models.Post.Rating -{ - public class ReadPostRatingServiceModel - { - public Guid Id { get; set; } - - public Guid PostId { get; set; } - - public int Likes { get; set; } - - public int Dislikes { get; set; } - } -} diff --git a/src/DevHive.Services/Options/JWTOptions.cs b/src/DevHive.Services/Options/JWTOptions.cs deleted file mode 100644 index 95458f5..0000000 --- a/src/DevHive.Services/Options/JWTOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.Extensions.Options; - -namespace DevHive.Services.Options -{ - public class JWTOptions - { - public JWTOptions(string secret) - { - this.Secret = secret; - } - - public string Secret { get; init; } - } -} diff --git a/src/DevHive.Services/Services/CloudinaryService.cs b/src/DevHive.Services/Services/CloudinaryService.cs deleted file mode 100644 index 57955a2..0000000 --- a/src/DevHive.Services/Services/CloudinaryService.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using CloudinaryDotNet; -using CloudinaryDotNet.Actions; -using DevHive.Services.Interfaces; -using Microsoft.AspNetCore.Http; - -namespace DevHive.Services.Services -{ - public class CloudinaryService : ICloudService - { - private readonly Cloudinary _cloudinary; - - public CloudinaryService(string cloudName, string apiKey, string apiSecret) - { - this._cloudinary = new Cloudinary(new Account(cloudName, apiKey, apiSecret)); - } - - public async Task<List<string>> UploadFilesToCloud(List<IFormFile> formFiles) - { - List<string> fileUrls = new(); - foreach (var formFile in formFiles) - { - string formFileId = Guid.NewGuid().ToString(); - - using (var ms = new MemoryStream()) - { - formFile.CopyTo(ms); - byte[] formBytes = ms.ToArray(); - - RawUploadParams rawUploadParams = new() - { - File = new FileDescription(formFileId, new MemoryStream(formBytes)), - PublicId = formFileId, - UseFilename = true - }; - - RawUploadResult rawUploadResult = await this._cloudinary.UploadAsync(rawUploadParams); - fileUrls.Add(rawUploadResult.Url.AbsoluteUri); - } - } - - return fileUrls; - } - - public async Task<bool> RemoveFilesFromCloud(List<string> fileUrls) - { - return true; - } - } -} diff --git a/src/DevHive.Services/Services/RateService.cs b/src/DevHive.Services/Services/RateService.cs deleted file mode 100644 index 204c550..0000000 --- a/src/DevHive.Services/Services/RateService.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Data.Interfaces.Repositories; -using DevHive.Data.Models; -using DevHive.Services.Interfaces; -using DevHive.Services.Models.Post.Rating; - -namespace DevHive.Services.Services -{ - public class RateService : IRateService - { - private readonly IPostRepository _postRepository; - private readonly IUserRepository _userRepository; - private readonly IRatingRepository _ratingRepository; - private readonly IMapper _mapper; - - public RateService(IPostRepository postRepository, IRatingRepository ratingRepository, IUserRepository userRepository, IMapper mapper) - { - this._postRepository = postRepository; - this._ratingRepository = ratingRepository; - this._userRepository = userRepository; - this._mapper = mapper; - } - - public async Task<ReadPostRatingServiceModel> RatePost(RatePostServiceModel ratePostServiceModel) - { - throw new NotImplementedException(); - // if (!await this._postRepository.DoesPostExist(ratePostServiceModel.PostId)) - // throw new ArgumentException("Post does not exist!"); - - // if (!await this._userRepository.DoesUserExistAsync(ratePostServiceModel.UserId)) - // throw new ArgumentException("User does not exist!"); - - // Post post = await this._postRepository.GetByIdAsync(ratePostServiceModel.PostId); - // User user = await this._userRepository.GetByIdAsync(ratePostServiceModel.UserId); - - // if (this.HasUserRatedThisPost(user, post)) - // throw new ArgumentException("You can't rate the same post more then one(duh, amigo)"); - - // this.Rate(user, post, ratePostServiceModel.Liked); - - // bool success = await this._ratingRepository.EditAsync(post.Rating.Id, post.Rating); - // if (!success) - // throw new InvalidOperationException("Unable to rate the post!"); - - // Rating newRating = await this._ratingRepository.GetByIdAsync(post.Rating.Id); - // return this._mapper.Map<ReadPostRatingServiceModel>(newRating); - } - - public async Task<ReadPostRatingServiceModel> RemoveUserRateFromPost(Guid userId, Guid postId) - { - throw new NotImplementedException(); - // Post post = await this._postRepository.GetByIdAsync(postId); - // User user = await this._userRepository.GetByIdAsync(userId); - - // if (!this.HasUserRatedThisPost(user, post)) - // throw new ArgumentException("You haven't rated this post, lmao!"); - } - - public bool HasUserRatedThisPost(User user, Post post) - { - throw new NotImplementedException(); - // return post.Rating.UsersThatRated - // .Any(x => x.Id == user.Id); - } - - private void Rate(User user, Post post, bool liked) - { - throw new NotImplementedException(); - // if (liked) - // post.Rating.Rate++; - // else - // post.Rating.Rate--; - - // post.Rating.UsersThatRated.Add(user); - } - } -} diff --git a/src/DevHive.Services/Services/UserService.cs b/src/DevHive.Services/Services/UserService.cs deleted file mode 100644 index 22d5052..0000000 --- a/src/DevHive.Services/Services/UserService.cs +++ /dev/null @@ -1,382 +0,0 @@ -using AutoMapper; -using DevHive.Services.Options; -using DevHive.Services.Models.Identity.User; -using System.Threading.Tasks; -using DevHive.Data.Models; -using System; -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; -using Microsoft.IdentityModel.Tokens; -using System.Text; -using System.Collections.Generic; -using DevHive.Common.Models.Identity; -using DevHive.Services.Interfaces; -using DevHive.Data.Interfaces.Repositories; -using System.Linq; -using DevHive.Common.Models.Misc; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; - -namespace DevHive.Services.Services -{ - public class UserService : IUserService - { - private readonly IUserRepository _userRepository; - private readonly IRoleRepository _roleRepository; - private readonly ILanguageRepository _languageRepository; - private readonly ITechnologyRepository _technologyRepository; - private readonly UserManager<User> _userManager; - private readonly RoleManager<Role> _roleManager; - private readonly IMapper _userMapper; - private readonly JWTOptions _jwtOptions; - private readonly ICloudService _cloudService; - - public UserService(IUserRepository userRepository, - ILanguageRepository languageRepository, - IRoleRepository roleRepository, - ITechnologyRepository technologyRepository, - UserManager<User> userManager, - RoleManager<Role> roleManager, - IMapper mapper, - JWTOptions jwtOptions, - ICloudService cloudService) - { - this._userRepository = userRepository; - this._roleRepository = roleRepository; - this._userMapper = mapper; - this._jwtOptions = jwtOptions; - this._languageRepository = languageRepository; - this._technologyRepository = technologyRepository; - this._userManager = userManager; - this._roleManager = roleManager; - this._cloudService = cloudService; - } - - #region Authentication - /// <summary> - /// Adds a new user to the database with the values from the given model. - /// Returns a JSON Web Token (that can be used for authorization) - /// </summary> - public async Task<TokenModel> LoginUser(LoginServiceModel loginModel) - { - if (!await this._userRepository.DoesUsernameExistAsync(loginModel.UserName)) - throw new ArgumentException("Invalid username!"); - - User user = await this._userRepository.GetByUsernameAsync(loginModel.UserName); - - if (user.PasswordHash != PasswordModifications.GeneratePasswordHash(loginModel.Password)) - throw new ArgumentException("Incorrect password!"); - - return new TokenModel(WriteJWTSecurityToken(user.Id, user.UserName, user.Roles)); - } - - /// <summary> - /// Returns a new JSON Web Token (that can be used for authorization) for the given user - /// </summary> - public async Task<TokenModel> RegisterUser(RegisterServiceModel registerModel) - { - if (await this._userRepository.DoesUsernameExistAsync(registerModel.UserName)) - throw new ArgumentException("Username already exists!"); - - if (await this._userRepository.DoesEmailExistAsync(registerModel.Email)) - throw new ArgumentException("Email already exists!"); - - User user = this._userMapper.Map<User>(registerModel); - user.PasswordHash = PasswordModifications.GeneratePasswordHash(registerModel.Password); - user.ProfilePicture = new ProfilePicture() { PictureURL = "/assets/images/feed/profile-pic.png" }; - - // Make sure the default role exists - //TODO: Move when project starts - if (!await this._roleRepository.DoesNameExist(Role.DefaultRole)) - await this._roleRepository.AddAsync(new Role { Name = Role.DefaultRole }); - - user.Roles.Add(await this._roleRepository.GetByNameAsync(Role.DefaultRole)); - - IdentityResult userResult = await this._userManager.CreateAsync(user); - User createdUser = await this._userRepository.GetByUsernameAsync(registerModel.UserName); - - if (!userResult.Succeeded) - throw new ArgumentException("Unable to create a user"); - - return new TokenModel(WriteJWTSecurityToken(createdUser.Id, createdUser.UserName, createdUser.Roles)); - } - #endregion - - #region Read - public async Task<UserServiceModel> GetUserById(Guid id) - { - User user = await this._userRepository.GetByIdAsync(id) ?? - throw new ArgumentException("User does not exist!"); - - return this._userMapper.Map<UserServiceModel>(user); - } - - public async Task<UserServiceModel> GetUserByUsername(string username) - { - User user = await this._userRepository.GetByUsernameAsync(username) ?? - throw new ArgumentException("User does not exist!"); - - return this._userMapper.Map<UserServiceModel>(user); - } - #endregion - - #region Update - public async Task<UserServiceModel> UpdateUser(UpdateUserServiceModel updateUserServiceModel) - { - await this.ValidateUserOnUpdate(updateUserServiceModel); - - User currentUser = await this._userRepository.GetByIdAsync(updateUserServiceModel.Id); - await this.PopulateUserModel(currentUser, updateUserServiceModel); - - if (updateUserServiceModel.Friends.Count() > 0) - await this.CreateRelationToFriends(currentUser, updateUserServiceModel.Friends.ToList()); - else - currentUser.Friends.Clear(); - - IdentityResult result = await this._userManager.UpdateAsync(currentUser); - - if (!result.Succeeded) - throw new InvalidOperationException("Unable to edit user!"); - - User newUser = await this._userRepository.GetByIdAsync(currentUser.Id); - return this._userMapper.Map<UserServiceModel>(newUser); - } - - /// <summary> - /// Uploads the given picture and assigns it's link to the user in the database - /// </summary> - public async Task<ProfilePictureServiceModel> UpdateProfilePicture(UpdateProfilePictureServiceModel updateProfilePictureServiceModel) - { - User user = await this._userRepository.GetByIdAsync(updateProfilePictureServiceModel.UserId); - - if (!string.IsNullOrEmpty(user.ProfilePicture.PictureURL)) - { - bool success = await _cloudService.RemoveFilesFromCloud(new List<string> { user.ProfilePicture.PictureURL }); - if (!success) - throw new InvalidCastException("Could not delete old profile picture!"); - } - - string fileUrl = (await this._cloudService.UploadFilesToCloud(new List<IFormFile> { updateProfilePictureServiceModel.Picture }))[0] ?? - throw new ArgumentNullException("Unable to upload profile picture to cloud"); - - bool successful = await this._userRepository.UpdateProfilePicture(updateProfilePictureServiceModel.UserId, fileUrl); - - if (!successful) - throw new InvalidOperationException("Unable to change profile picture!"); - - return new ProfilePictureServiceModel() { ProfilePictureURL = fileUrl }; - } - #endregion - - #region Delete - public async Task<bool> DeleteUser(Guid id) - { - if (!await this._userRepository.DoesUserExistAsync(id)) - throw new ArgumentException("User does not exist!"); - - User user = await this._userRepository.GetByIdAsync(id); - IdentityResult result = await this._userManager.DeleteAsync(user); - - return result.Succeeded; - } - #endregion - - #region Validations - /// <summary> - /// Checks whether the given user, gotten by the "id" property, - /// is the same user as the one in the token (uness the user in the token has the admin role) - /// and the roles in the token are the same as those in the user, gotten by the id in the token - /// </summary> - public async Task<bool> ValidJWT(Guid id, string rawTokenData) - { - // There is authorization name in the beginning, i.e. "Bearer eyJh..." - var jwt = new JwtSecurityTokenHandler().ReadJwtToken(rawTokenData.Remove(0, 7)); - - Guid jwtUserID = new Guid(this.GetClaimTypeValues("ID", jwt.Claims).First()); - List<string> jwtRoleNames = this.GetClaimTypeValues("role", jwt.Claims); - - User user = await this._userRepository.GetByIdAsync(jwtUserID) - ?? throw new ArgumentException("User does not exist!"); - - /* Check if user is trying to do something to himself, unless he's an admin */ - - /* Check roles */ - if (!jwtRoleNames.Contains(Role.AdminRole)) - if (user.Id != id) - return false; - - // Check if jwt contains all user roles (if it doesn't, jwt is either old or tampered with) - foreach (var role in user.Roles) - { - if (!jwtRoleNames.Contains(role.Name)) - return false; - } - - // Check if jwt contains only roles of user - if (jwtRoleNames.Count != user.Roles.Count) - return false; - - return true; - } - - /// <summary> - /// Returns all values from a given claim type - /// </summary> - private List<string> GetClaimTypeValues(string type, IEnumerable<Claim> claims) - { - List<string> toReturn = new(); - - foreach (var claim in claims) - if (claim.Type == type) - toReturn.Add(claim.Value); - - return toReturn; - } - - /// <summary> - /// Checks whether the user in the model exists - /// and whether the username in the model is already taken. - /// If the check fails (is false), it throws an exception, otherwise nothing happens - /// </summary> - private async Task ValidateUserOnUpdate(UpdateUserServiceModel updateUserServiceModel) - { - if (!await this._userRepository.DoesUserExistAsync(updateUserServiceModel.Id)) - throw new ArgumentException("User does not exist!"); - - if (updateUserServiceModel.Friends.Any(x => x.UserName == updateUserServiceModel.UserName)) - throw new ArgumentException("You cant add yourself as a friend(sry, bro)!"); - - if (!this._userRepository.DoesUserHaveThisUsername(updateUserServiceModel.Id, updateUserServiceModel.UserName) - && await this._userRepository.DoesUsernameExistAsync(updateUserServiceModel.UserName)) - throw new ArgumentException("Username already exists!"); - - List<string> usernames = new(); - foreach (var friend in updateUserServiceModel.Friends) - usernames.Add(friend.UserName); - - if (!await this._userRepository.ValidateFriendsCollectionAsync(usernames)) - throw new ArgumentException("One or more friends do not exist!"); - } - - /// <summary> - /// Return a new JSON Web Token, containing the user id, username and roles. - /// Tokens have an expiration time of 7 days. - /// </summary> - private string WriteJWTSecurityToken(Guid userId, string username, HashSet<Role> roles) - { - byte[] signingKey = Encoding.ASCII.GetBytes(_jwtOptions.Secret); - HashSet<Claim> claims = new() - { - new Claim("ID", $"{userId}"), - new Claim("Username", username) - }; - - foreach (var role in roles) - { - claims.Add(new Claim(ClaimTypes.Role, role.Name)); - } - - SecurityTokenDescriptor tokenDescriptor = new() - { - Subject = new ClaimsIdentity(claims), - Expires = DateTime.Today.AddDays(7), - SigningCredentials = new SigningCredentials( - new SymmetricSecurityKey(signingKey), - SecurityAlgorithms.HmacSha512Signature) - }; - - JwtSecurityTokenHandler tokenHandler = new(); - SecurityToken token = tokenHandler.CreateToken(tokenDescriptor); - return tokenHandler.WriteToken(token); - } - #endregion - - #region Misc - public async Task<TokenModel> SuperSecretPromotionToAdmin(Guid userId) - { - User user = await this._userRepository.GetByIdAsync(userId) ?? - throw new ArgumentException("User does not exist! Can't promote shit in this country..."); - - if (!await this._roleRepository.DoesNameExist(Role.AdminRole)) - { - Role adminRole = new() - { - Name = Role.AdminRole - }; - adminRole.Users.Add(user); - - await this._roleRepository.AddAsync(adminRole); - } - - Role admin = await this._roleManager.FindByNameAsync(Role.AdminRole); - - user.Roles.Add(admin); - await this._userManager.UpdateAsync(user); - - User newUser = await this._userRepository.GetByIdAsync(userId); - - return new TokenModel(WriteJWTSecurityToken(newUser.Id, newUser.UserName, newUser.Roles)); - } - - private async Task PopulateUserModel(User user, UpdateUserServiceModel updateUserServiceModel) - { - user.UserName = updateUserServiceModel.UserName; - user.FirstName = updateUserServiceModel.FirstName; - user.LastName = updateUserServiceModel.LastName; - user.Email = updateUserServiceModel.Email; - - //Do NOT allow a user to change his roles, unless he is an Admin - bool isAdmin = await this._userManager.IsInRoleAsync(user, Role.AdminRole); - - if (isAdmin) - { - HashSet<Role> roles = new(); - foreach (var role in updateUserServiceModel.Roles) - { - Role returnedRole = await this._roleRepository.GetByNameAsync(role.Name) ?? - throw new ArgumentException($"Role {role.Name} does not exist!"); - - roles.Add(returnedRole); - } - user.Roles = roles; - } - - HashSet<Language> languages = new(); - int languagesCount = updateUserServiceModel.Languages.Count; - for (int i = 0; i < languagesCount; i++) - { - Language language = await this._languageRepository.GetByNameAsync(updateUserServiceModel.Languages.ElementAt(i).Name) ?? - throw new ArgumentException("Invalid language name!"); - - languages.Add(language); - } - user.Languages = languages; - - /* Fetch Technologies and replace model's*/ - HashSet<Technology> technologies = new(); - int technologiesCount = updateUserServiceModel.Technologies.Count; - for (int i = 0; i < technologiesCount; i++) - { - Technology technology = await this._technologyRepository.GetByNameAsync(updateUserServiceModel.Technologies.ElementAt(i).Name) ?? - throw new ArgumentException("Invalid technology name!"); - - technologies.Add(technology); - } - user.Technologies = technologies; - } - - private async Task CreateRelationToFriends(User user, List<UpdateFriendServiceModel> friends) - { - foreach (var friend in friends) - { - User amigo = await this._userRepository.GetByUsernameAsync(friend.UserName); - - user.Friends.Add(amigo); - amigo.Friends.Add(user); - - await this._userManager.UpdateAsync(amigo); - } - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Data.Tests/CommentRepository.Tests.cs b/src/DevHive.Tests/DevHive.Data.Tests/CommentRepository.Tests.cs deleted file mode 100644 index 9cbb43b..0000000 --- a/src/DevHive.Tests/DevHive.Data.Tests/CommentRepository.Tests.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Threading.Tasks; -using DevHive.Data.Models; -using DevHive.Data.Repositories; -using Microsoft.EntityFrameworkCore; -using NUnit.Framework; - -namespace DevHive.Data.Tests -{ - [TestFixture] - public class CommentRepositoryTests - { - private const string COMMENT_MESSAGE = "Comment message"; - - protected DevHiveContext Context { get; set; } - - protected CommentRepository CommentRepository { get; set; } - - #region Setups - [SetUp] - public void Setup() - { - var optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() - .UseInMemoryDatabase(databaseName: "DevHive_Test_Database"); - - this.Context = new DevHiveContext(optionsBuilder.Options); - - CommentRepository = new CommentRepository(Context); - } - - [TearDown] - public void TearDown() - { - this.Context.Database.EnsureDeleted(); - } - #endregion - - #region GetCommentByIssuerAndTimeCreatedAsync - [Test] - public async Task GetCommentByCreatorAndTimeCreatedAsync_ReturnsTheCorrectComment_IfItExists() - { - Comment comment = await this.AddEntity(); - - Comment resultComment = await this.CommentRepository.GetCommentByIssuerAndTimeCreatedAsync(comment.Creator.Id, comment.TimeCreated); - - Assert.AreEqual(comment.Id, resultComment.Id, "GetCommentByIssuerAndTimeCreatedAsync does not return the corect comment when it exists"); - } - - [Test] - public async Task GetPostByCreatorAndTimeCreatedAsync_ReturnsNull_IfThePostDoesNotExist() - { - Comment comment = await this.AddEntity(); - - Comment resultComment = await this.CommentRepository.GetCommentByIssuerAndTimeCreatedAsync(Guid.Empty, DateTime.Now); - - Assert.IsNull(resultComment, "GetCommentByIssuerAndTimeCreatedAsync does not return null when the comment does not exist"); - } - #endregion - - #region DoesCommentExist - [Test] - public async Task DoesCommentExist_ReturnsTrue_WhenTheCommentExists() - { - Comment comment = await this.AddEntity(); - - bool result = await this.CommentRepository.DoesCommentExist(comment.Id); - - Assert.IsTrue(result, "DoesCommentExist does not return true whenm the Comment exists"); - } - - [Test] - public async Task DoesCommentExist_ReturnsFalse_WhenTheCommentDoesNotExist() - { - bool result = await this.CommentRepository.DoesCommentExist(Guid.Empty); - - Assert.IsFalse(result, "DoesCommentExist does not return false whenm the Comment" + - " does not exist"); - } - #endregion - - #region HelperMethods - private async Task<Comment> AddEntity(string name = COMMENT_MESSAGE) - { - User creator = new User { Id = Guid.NewGuid() }; - Comment comment = new Comment - { - Message = COMMENT_MESSAGE, - Creator = creator, - TimeCreated = DateTime.Now - }; - - this.Context.Comments.Add(comment); - await this.Context.SaveChangesAsync(); - - return comment; - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Data.Tests/FeedRepository.Tests.cs b/src/DevHive.Tests/DevHive.Data.Tests/FeedRepository.Tests.cs deleted file mode 100644 index f134bf3..0000000 --- a/src/DevHive.Tests/DevHive.Data.Tests/FeedRepository.Tests.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using DevHive.Data.Models; -using DevHive.Data.Repositories; -using Microsoft.EntityFrameworkCore; -using NUnit.Framework; - -namespace DevHive.Data.Tests -{ - [TestFixture] - public class FeedRepositoryTests - { - protected DevHiveContext Context { get; set; } - - protected FeedRepository FeedRepository { get; set; } - - #region Setups - [SetUp] - public void Setup() - { - var optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() - .UseInMemoryDatabase(databaseName: "DevHive_Test_Database"); - - this.Context = new DevHiveContext(optionsBuilder.Options); - - FeedRepository = new FeedRepository(Context); - } - - [TearDown] - public void TearDown() - { - this.Context.Database.EnsureDeleted(); - } - #endregion - - #region GetFriendsPosts - [Test] - public async Task GetFriendsPosts_ReturnsListOfPosts_WhenTheyExist() - { - User dummyUser = this.CreateDummyUser(); - List<User> friendsList = new List<User>(); - friendsList.Add(dummyUser); - - DateTime dateTime = new DateTime(3000, 05, 09, 9, 15, 0); - Console.WriteLine(dateTime.ToFileTime()); - - Post dummyPost = this.CreateDummyPost(dummyUser); - Post anotherDummnyPost = this.CreateDummyPost(dummyUser); - - const int PAGE_NUMBER = 1; - const int PAGE_SIZE = 10; - - List<Post> resultList = await this.FeedRepository.GetFriendsPosts(friendsList, dateTime, PAGE_NUMBER, PAGE_SIZE); - - Assert.GreaterOrEqual(2, resultList.Count, "GetFriendsPosts does not return all correct posts"); - } - - [Test] - public async Task GetFriendsPosts_ReturnsNull_WhenNoSuitablePostsExist() - { - User dummyUser = this.CreateDummyUser(); - List<User> friendsList = new List<User>(); - friendsList.Add(dummyUser); - - DateTime dateTime = new DateTime(3000, 05, 09, 9, 15, 0); - - const int PAGE_NUMBER = 1; - const int PAGE_SIZE = 10; - - List<Post> resultList = await this.FeedRepository.GetFriendsPosts(friendsList, dateTime, PAGE_NUMBER, PAGE_SIZE); - - Assert.LessOrEqual(0, resultList.Count, "GetFriendsPosts does not return all correct posts"); - } - #endregion - - #region HelperMethods - private User CreateDummyUser() - { - HashSet<Role> roles = new() - { - new Role() - { - Id = Guid.NewGuid(), - Name = Role.DefaultRole - }, - }; - - return new() - { - Id = Guid.NewGuid(), - UserName = "pioneer10", - FirstName = "Spas", - LastName = "Spasov", - Email = "abv@abv.bg", - Roles = roles - }; - } - - private Post CreateDummyPost(User poster) - { - const string POST_MESSAGE = "random message"; - Guid id = Guid.NewGuid(); - Post post = new Post - { - Id = id, - Message = POST_MESSAGE, - Creator = poster, - TimeCreated = new DateTime(2000, 05, 09, 9, 15, 0) - }; - - this.Context.Posts.Add(post); - this.Context.SaveChanges(); - - return post; - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Data.Tests/RoleRepository.Tests.cs b/src/DevHive.Tests/DevHive.Data.Tests/RoleRepository.Tests.cs deleted file mode 100644 index 7f62c24..0000000 --- a/src/DevHive.Tests/DevHive.Data.Tests/RoleRepository.Tests.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using DevHive.Data.Models; -using DevHive.Data.Repositories; -using Microsoft.EntityFrameworkCore; -using NUnit.Framework; - -namespace DevHive.Data.Tests -{ - [TestFixture] - public class RoleRepositoryTests - { - private const string ROLE_NAME = "Role test name"; - - protected DevHiveContext Context { get; set; } - - protected RoleRepository RoleRepository { get; set; } - - #region Setups - [SetUp] - public void Setup() - { - var optionsBuilder = new DbContextOptionsBuilder<DevHiveContext>() - .UseInMemoryDatabase(databaseName: "DevHive_Test_Database"); - - this.Context = new DevHiveContext(optionsBuilder.Options); - - RoleRepository = new RoleRepository(Context); - } - - [TearDown] - public void TearDown() - { - this.Context.Database.EnsureDeleted(); - } - #endregion - - #region GetByNameAsync - [Test] - public async Task GetByNameAsync_ReturnsTheRole_WhenItExists() - { - Role role = await this.AddEntity(); - - Role resultRole = await this.RoleRepository.GetByNameAsync(role.Name); - - Assert.AreEqual(role.Id, resultRole.Id, "GetByNameAsync does not return the correct role"); - } - - [Test] - public async Task GetByNameAsync_ReturnsNull_WhenTheRoleDoesNotExist() - { - Role resultRole = await this.RoleRepository.GetByNameAsync(ROLE_NAME); - - Assert.IsNull(resultRole, "GetByNameAsync does not return when the role does not exist"); - } - #endregion - - #region DoesNameExist - [Test] - public async Task DoesNameExist_ReturnsTrue_WhenTheNameExists() - { - Role role = await this.AddEntity(); - - bool result = await this.RoleRepository.DoesNameExist(role.Name); - - Assert.IsTrue(result, "DoesNameExist returns false when the role name exist"); - } - - [Test] - public async Task DoesNameExist_ReturnsFalse_WhenTheNameDoesNotExist() - { - bool result = await this.RoleRepository.DoesNameExist(ROLE_NAME); - - Assert.IsFalse(result, "DoesNameExist returns false when the role name exist"); - } - #endregion - - #region DoesRoleExist - [Test] - public async Task DoesRoleExist_ReturnsTrue_IfIdExists() - { - await AddEntity(); - Role role = this.Context.Roles.Where(x => x.Name == ROLE_NAME).ToList().FirstOrDefault(); - Guid id = role.Id; - - bool result = await this.RoleRepository.DoesRoleExist(id); - - Assert.IsTrue(result, "DoesRoleExistAsync returns flase when role exists"); - } - - [Test] - public async Task DoesRoleExist_ReturnsFalse_IfIdDoesNotExists() - { - Guid id = Guid.NewGuid(); - - bool result = await this.RoleRepository.DoesRoleExist(id); - - Assert.IsFalse(result, "DoesRoleExist returns true when role does not exist"); - } - #endregion - - #region HelperMethods - private async Task<Role> AddEntity(string name = ROLE_NAME) - { - Role role = new Role - { - Id = Guid.NewGuid(), - Name = name - }; - - this.Context.Roles.Add(role); - await this.Context.SaveChangesAsync(); - - return role; - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Data.Tests/UserRepositoryTests.cs b/src/DevHive.Tests/DevHive.Data.Tests/UserRepositoryTests.cs deleted file mode 100644 index 43e9a36..0000000 --- a/src/DevHive.Tests/DevHive.Data.Tests/UserRepositoryTests.cs +++ /dev/null @@ -1,350 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using DevHive.Data.Models; -using DevHive.Data.Repositories; -using Microsoft.EntityFrameworkCore; -using NUnit.Framework; - -namespace DevHive.Data.Tests -{ - [TestFixture] - public class UserRepositoryTests - { - private DevHiveContext _context; - private UserRepository _userRepository; - - #region Setups - [SetUp] - public void Setup() - { - var options = new DbContextOptionsBuilder<DevHiveContext>() - .UseInMemoryDatabase("DevHive_UserRepository_Database"); - - this._context = new DevHiveContext(options.Options); - this._userRepository = new UserRepository(_context); - } - - [TearDown] - public async Task Teardown() - { - await this._context.Database.EnsureDeletedAsync(); - } - #endregion - - #region QueryAll - // [Test] - // public async Task QueryAll_ShouldReturnAllUsersFromDatabase_WhenTheyExist() - // { - // //Arrange - // User dummyUserOne = CreateDummyUser(); - // User dummyUserTwo = CreateAnotherDummyUser(); - // - // await this._userRepository.AddAsync(dummyUserOne); - // await this._userRepository.AddAsync(dummyUserTwo); - // - // //Act - // IEnumerable<User> users = this._userRepository.QueryAll(); - // - // //Assert - // Assert.AreEqual(2, users.Count(), "Method doesn't return all instances of user"); - // } - - // [Test] - // public void QueryAll_ReturnsNull_WhenNoUsersExist() - // { - // IEnumerable<User> users = this._userRepository.QueryAll(); - // - // Assert.AreEqual(0, users.Count(), "Method returns Users when there are non"); - // } - #endregion - - #region EditAsync - [Test] - public async Task EditAsync_ReturnsTrue_WhenUserIsUpdatedSuccessfully() - { - User oldUser = this.CreateDummyUser(); - this._context.Users.Add(oldUser); - await this._context.SaveChangesAsync(); - - oldUser.UserName = "SuperSecretUserName"; - bool result = await this._userRepository.EditAsync(oldUser.Id, oldUser); - - Assert.IsTrue(result, "EditAsync does not return true when User is updated successfully"); - } - #endregion - - #region GetByIdAsync - [Test] - public async Task GetByIdAsync_ReturnsTheUse_WhenItExists() - { - User dummyUserOne = CreateDummyUser(); - await this._userRepository.AddAsync(dummyUserOne); - - User resultUser = await this._userRepository.GetByIdAsync(dummyUserOne.Id); - - Assert.AreEqual(dummyUserOne.UserName, resultUser.UserName); - } - - [Test] - public async Task GetByIdAsync_ReturnsNull_WhenUserDoesNotExist() - { - Guid id = Guid.NewGuid(); - - User resultUser = await this._userRepository.GetByIdAsync(id); - - Assert.IsNull(resultUser); - } - #endregion - - #region GetByUsernameAsync - [Test] - public async Task GetByUsernameAsync_ReturnsUserFromDatabase_WhenItExists() - { - //Arrange - User dummyUser = CreateDummyUser(); - await this._userRepository.AddAsync(dummyUser); - string username = dummyUser.UserName; - - //Act - User user = await this._userRepository.GetByUsernameAsync(username); - - //Assert - Assert.AreEqual(dummyUser.Id, user.Id, "Method doesn't get the proper user from database"); - } - - [Test] - public async Task GetByUsernameAsync_ReturnsNull_WhenUserDoesNotExist() - { - //Act - User user = await this._userRepository.GetByUsernameAsync(null); - - //Assert - Assert.IsNull(user, "Method returns user when it does not exist"); - } - #endregion - - #region DoesUserExistAsync - [Test] - public async Task DoesUserExistAsync_ReturnsTrue_WhenUserExists() - { - User dummyUser = this.CreateDummyUser(); - this._context.Users.Add(dummyUser); - await this._context.SaveChangesAsync(); - - bool result = await this._userRepository.DoesUserExistAsync(dummyUser.Id); - - Assert.IsTrue(result, "DoesUserExistAsync does not return true when user exists"); - } - - [Test] - public async Task DoesUserExistAsync_ReturnsFalse_WhenUserDoesNotExist() - { - Guid id = Guid.NewGuid(); - - bool result = await this._userRepository.DoesUserExistAsync(id); - - Assert.IsFalse(result, "DoesUserExistAsync does not return false when user does not exist"); - } - #endregion - - #region DoesUserNameExistAsync - [Test] - public async Task DoesUsernameExistAsync_ReturnsTrue_WhenUserWithTheNameExists() - { - User dummyUser = this.CreateDummyUser(); - this._context.Users.Add(dummyUser); - await this._context.SaveChangesAsync(); - - bool result = await this._userRepository.DoesUsernameExistAsync(dummyUser.UserName); - - Assert.IsTrue(result, "DoesUserNameExistAsync does not return true when username exists"); - } - - [Test] - public async Task DoesUsernameExistAsync_ReturnsFalse_WhenUserWithTheNameDoesNotExist() - { - string userName = "Fake name"; - - bool result = await this._userRepository.DoesUsernameExistAsync(userName); - - Assert.IsFalse(result, "DoesUserNameExistAsync does not return false when username does not exist"); - } - #endregion - - #region DoesEmailExistAsync - [Test] - public async Task DoesEmailExistAsync_ReturnsTrue_WhenUserWithTheEmailExists() - { - User dummyUser = this.CreateDummyUser(); - this._context.Users.Add(dummyUser); - await this._context.SaveChangesAsync(); - - bool result = await this._userRepository.DoesEmailExistAsync(dummyUser.Email); - - Assert.IsTrue(result, "DoesUserNameExistAsync does not return true when email exists"); - } - - [Test] - public async Task DoesEmailExistAsync_ReturnsFalse_WhenUserWithTheEmailDoesNotExist() - { - string email = "Fake email"; - - bool result = await this._userRepository.DoesUsernameExistAsync(email); - - Assert.IsFalse(result, "DoesUserNameExistAsync does not return false when email does not exist"); - } - #endregion - - #region DoesUserHaveThisFriendAsync - //[Test] - //public async Task DoesUserHaveThisFriendAsync_ReturnsTrue_WhenUserHasTheGivenFriend() - //{ - // User dummyUser = this.CreateDummyUser(); - // User anotherDummyUser = this.CreateAnotherDummyUser(); - // HashSet<User> friends = new HashSet<User> - // { - // anotherDummyUser - // }; - // dummyUser.Friends = friends; - - // this._context.Users.Add(dummyUser); - // this._context.Users.Add(anotherDummyUser); - // await this._context.SaveChangesAsync(); - - // bool result = await this._userRepository.DoesUserHaveThisFriendAsync(dummyUser.Id, anotherDummyUser.Id); - - // Assert.IsTrue(result, "DoesUserHaveThisFriendAsync does not return true when user has the given friend"); - //} - - // [Test] - // public async Task DoesUserHaveThisFriendAsync_ReturnsFalse_WhenUserDoesNotHaveTheGivenFriend() - // { - // User dummyUser = this.CreateDummyUser(); - // User anotherDummyUser = this.CreateAnotherDummyUser(); - // - // this._context.Users.Add(dummyUser); - // this._context.Users.Add(anotherDummyUser); - // await this._context.SaveChangesAsync(); - // - // bool result = await this._userRepository.DoesUserHaveThisFriendAsync(dummyUser.Id, anotherDummyUser.Id); - // - // Assert.IsFalse(result, "DoesUserHaveThisFriendAsync does not return false when user des not have the given friend"); - // } - #endregion - - #region DoesUserHaveThisUsername - [Test] - public async Task DoesUserHaveThisUsername_ReturnsTrue_WhenUserHasTheGivenUsername() - { - User dummyUser = this.CreateDummyUser(); - this._context.Users.Add(dummyUser); - await this._context.SaveChangesAsync(); - - bool result = this._userRepository.DoesUserHaveThisUsername(dummyUser.Id, dummyUser.UserName); - - Assert.IsTrue(result, "DoesUserHaveThisUsername does not return true when the user has the given name"); - } - - [Test] - public async Task DoesUserHaveThisUsername_ReturnsFalse_WhenUserDoesntHaveTheGivenUsername() - { - string username = "Fake username"; - User dummyUser = this.CreateDummyUser(); - this._context.Users.Add(dummyUser); - await this._context.SaveChangesAsync(); - - bool result = this._userRepository.DoesUserHaveThisUsername(dummyUser.Id, username); - - Assert.IsFalse(result, "DoesUserNameExistAsync does not return false when user doesnt have the given name"); - } - #endregion - - #region HelperMethods - private User CreateDummyUser() - { - HashSet<Language> languages = new() - { - new Language() - { - Id = Guid.NewGuid(), - Name = "csharp" - }, - }; - - HashSet<Technology> technologies = new() - { - new Technology() - { - Id = Guid.NewGuid(), - Name = "ASP.NET Core" - }, - }; - - HashSet<Role> roles = new() - { - new Role() - { - Id = Guid.NewGuid(), - Name = Role.DefaultRole - }, - }; - - return new() - { - Id = Guid.NewGuid(), - UserName = "dummyUser", - FirstName = "Spas", - LastName = "Spasov", - Email = "abv@abv.bg", - Languages = languages, - Technologies = technologies, - Roles = roles - }; - } - - private User CreateAnotherDummyUser() - { - HashSet<Language> languages = new() - { - new Language() - { - Id = Guid.NewGuid(), - Name = "typescript" - }, - }; - - HashSet<Technology> technologies = new() - { - new Technology() - { - Id = Guid.NewGuid(), - Name = "Angular" - }, - }; - - HashSet<Role> roles = new() - { - new Role() - { - Id = Guid.NewGuid(), - Name = Role.DefaultRole - }, - }; - - return new() - { - Id = Guid.NewGuid(), - UserName = "anotherDummyUser", - FirstName = "Alex", - LastName = "Spiridonov", - Email = "a_spiridonov@abv.bg", - Languages = languages, - Technologies = technologies, - Roles = roles - }; - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Services.Tests/CommentService.Tests.cs b/src/DevHive.Tests/DevHive.Services.Tests/CommentService.Tests.cs deleted file mode 100644 index ac022ea..0000000 --- a/src/DevHive.Tests/DevHive.Services.Tests/CommentService.Tests.cs +++ /dev/null @@ -1,262 +0,0 @@ -using System; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Data.Interfaces.Repositories; -using DevHive.Data.Models; -using DevHive.Services.Models.Comment; -using DevHive.Services.Services; -using Moq; -using NUnit.Framework; - -namespace DevHive.Services.Tests -{ - [TestFixture] - public class CommentServiceTests - { - private const string MESSAGE = "Gosho Trapov"; - private Mock<IUserRepository> UserRepositoryMock { get; set; } - private Mock<IPostRepository> PostRepositoryMock { get; set; } - private Mock<ICommentRepository> CommentRepositoryMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private CommentService CommentService { get; set; } - - #region Setup - [SetUp] - public void Setup() - { - this.UserRepositoryMock = new Mock<IUserRepository>(); - this.PostRepositoryMock = new Mock<IPostRepository>(); - this.CommentRepositoryMock = new Mock<ICommentRepository>(); - this.MapperMock = new Mock<IMapper>(); - this.CommentService = new CommentService(this.UserRepositoryMock.Object, this.PostRepositoryMock.Object, this.CommentRepositoryMock.Object, this.MapperMock.Object); - } - #endregion - - #region AddComment - [Test] - public async Task AddComment_ReturnsNonEmptyGuid_WhenEntityIsAddedSuccessfully() - { - Guid id = Guid.NewGuid(); - User creator = new User { Id = Guid.NewGuid() }; - CreateCommentServiceModel createCommentServiceModel = new CreateCommentServiceModel - { - Message = MESSAGE - }; - Comment comment = new Comment - { - Message = MESSAGE, - Id = id, - }; - - this.CommentRepositoryMock.Setup(p => p.AddAsync(It.IsAny<Comment>())).Returns(Task.FromResult(true)); - this.CommentRepositoryMock.Setup(p => p.GetCommentByIssuerAndTimeCreatedAsync(It.IsAny<Guid>(), It.IsAny<DateTime>())).Returns(Task.FromResult(comment)); - this.PostRepositoryMock.Setup(p => p.DoesPostExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.UserRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(creator)); - this.MapperMock.Setup(p => p.Map<Comment>(It.IsAny<CreateCommentServiceModel>())).Returns(comment); - - Guid result = await this.CommentService.AddComment(createCommentServiceModel); - - Assert.AreEqual(id, result); - } - - [Test] - public async Task AddComment_ReturnsEmptyGuid_WhenEntityIsNotAddedSuccessfully() - { - CreateCommentServiceModel createCommentServiceModel = new CreateCommentServiceModel - { - Message = MESSAGE - }; - Comment comment = new Comment - { - Message = MESSAGE, - }; - - this.CommentRepositoryMock.Setup(p => p.AddAsync(It.IsAny<Comment>())).Returns(Task.FromResult(false)); - this.PostRepositoryMock.Setup(p => p.DoesPostExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.MapperMock.Setup(p => p.Map<Comment>(It.IsAny<CreateCommentServiceModel>())).Returns(comment); - - Guid result = await this.CommentService.AddComment(createCommentServiceModel); - - Assert.IsTrue(result == Guid.Empty); - } - - [Test] - public void AddComment_ThrowsException_WhenPostDoesNotExist() - { - const string EXCEPTION_MESSAGE = "Post does not exist!"; - - CreateCommentServiceModel createCommentServiceModel = new CreateCommentServiceModel - { - Message = MESSAGE - }; - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.CommentService.AddComment(createCommentServiceModel), "AddComment does not throw excpeion when the post does not exist"); - - Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorecct exception message"); - } - #endregion - - #region GetCommentById - [Test] - public async Task GetCommentById_ReturnsTheComment_WhenItExists() - { - Guid creatorId = new Guid(); - User creator = new User { Id = creatorId }; - Comment comment = new Comment - { - Message = MESSAGE, - Creator = creator - }; - ReadCommentServiceModel commentServiceModel = new ReadCommentServiceModel - { - Message = MESSAGE - }; - User user = new User - { - Id = creatorId, - }; - - this.CommentRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(comment)); - this.UserRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(user)); - this.MapperMock.Setup(p => p.Map<ReadCommentServiceModel>(It.IsAny<Comment>())).Returns(commentServiceModel); - - ReadCommentServiceModel result = await this.CommentService.GetCommentById(new Guid()); - - Assert.AreEqual(MESSAGE, result.Message); - } - - [Test] - public void GetCommentById_ThorwsException_WhenTheUserDoesNotExist() - { - const string EXCEPTION_MESSAGE = "The user does not exist"; - Guid creatorId = new Guid(); - User creator = new User { Id = creatorId }; - Comment comment = new Comment - { - Message = MESSAGE, - Creator = creator - }; - - this.CommentRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(comment)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.CommentService.GetCommentById(new Guid()), "GetCommentById does not throw exception when the user does not exist"); - - Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message); - } - - [Test] - public void GetCommentById_ThrowsException_WhenCommentDoesNotExist() - { - string exceptionMessage = "The comment does not exist"; - Guid creatorId = new Guid(); - User user = new User - { - Id = creatorId, - }; - - this.CommentRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult<Comment>(null)); - this.UserRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(user)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.CommentService.GetCommentById(new Guid())); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region UpdateComment - [Test] - public async Task UpdateComment_ReturnsTheIdOfTheComment_WhenUpdatedSuccessfully() - { - Guid id = Guid.NewGuid(); - Comment comment = new Comment - { - Id = id, - Message = MESSAGE - }; - UpdateCommentServiceModel updateCommentServiceModel = new UpdateCommentServiceModel - { - CommentId = id, - NewMessage = MESSAGE - }; - - this.CommentRepositoryMock.Setup(p => p.DoesCommentExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.CommentRepositoryMock.Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Comment>())).Returns(Task.FromResult(true)); - this.CommentRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(comment)); - this.MapperMock.Setup(p => p.Map<Comment>(It.IsAny<UpdateCommentServiceModel>())).Returns(comment); - - Guid result = await this.CommentService.UpdateComment(updateCommentServiceModel); - - Assert.AreEqual(updateCommentServiceModel.CommentId, result); - } - - [Test] - public async Task UpdateComment_ReturnsEmptyId_WhenTheCommentIsNotUpdatedSuccessfully() - { - Comment comment = new Comment - { - Message = MESSAGE - }; - UpdateCommentServiceModel updateCommentServiceModel = new UpdateCommentServiceModel - { - CommentId = Guid.NewGuid(), - NewMessage = MESSAGE - }; - - this.CommentRepositoryMock.Setup(p => p.DoesCommentExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.CommentRepositoryMock.Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Comment>())).Returns(Task.FromResult(false)); - this.MapperMock.Setup(p => p.Map<Comment>(It.IsAny<UpdateCommentServiceModel>())).Returns(comment); - - Guid result = await this.CommentService.UpdateComment(updateCommentServiceModel); - - Assert.AreEqual(Guid.Empty, result); - } - - [Test] - public void UpdateComment_ThrowsArgumentException_WhenCommentDoesNotExist() - { - string exceptionMessage = "Comment does not exist!"; - UpdateCommentServiceModel updateCommentServiceModel = new UpdateCommentServiceModel - { - }; - - this.CommentRepositoryMock.Setup(p => p.DoesCommentExist(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.CommentService.UpdateComment(updateCommentServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region DeleteComment - [Test] - [TestCase(true)] - [TestCase(false)] - public async Task DeleteComment_ShouldReturnIfDeletionIsSuccessfull_WhenCommentExists(bool shouldPass) - { - Guid id = new Guid(); - Comment comment = new Comment(); - - this.CommentRepositoryMock.Setup(p => p.DoesCommentExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.CommentRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(comment)); - this.CommentRepositoryMock.Setup(p => p.DeleteAsync(It.IsAny<Comment>())).Returns(Task.FromResult(shouldPass)); - - bool result = await this.CommentService.DeleteComment(id); - - Assert.AreEqual(shouldPass, result); - } - - [Test] - public void DeleteComment_ThrowsException_WhenCommentDoesNotExist() - { - string exceptionMessage = "Comment does not exist!"; - Guid id = new Guid(); - - this.CommentRepositoryMock.Setup(p => p.DoesCommentExist(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.CommentService.DeleteComment(id)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Services.Tests/LanguageService.Tests.cs b/src/DevHive.Tests/DevHive.Services.Tests/LanguageService.Tests.cs deleted file mode 100644 index 1b59f91..0000000 --- a/src/DevHive.Tests/DevHive.Services.Tests/LanguageService.Tests.cs +++ /dev/null @@ -1,262 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Data.Interfaces.Repositories; -using DevHive.Data.Models; -using DevHive.Services.Models.Language; -using DevHive.Services.Services; -using Moq; -using NUnit.Framework; - -namespace DevHive.Services.Tests -{ - [TestFixture] - public class LanguageServiceTests - { - private Mock<ILanguageRepository> LanguageRepositoryMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private LanguageService LanguageService { get; set; } - - #region SetUps - [SetUp] - public void SetUp() - { - this.LanguageRepositoryMock = new Mock<ILanguageRepository>(); - this.MapperMock = new Mock<IMapper>(); - this.LanguageService = new LanguageService(this.LanguageRepositoryMock.Object, this.MapperMock.Object); - } - #endregion - - #region CreateLanguage - [Test] - public async Task CreateLanguage_ReturnsNonEmptyGuid_WhenEntityIsAddedSuccessfully() - { - string technologyName = "Gosho Trapov"; - Guid id = Guid.NewGuid(); - CreateLanguageServiceModel createLanguageServiceModel = new CreateLanguageServiceModel - { - Name = technologyName - }; - Language language = new Language - { - Name = technologyName, - Id = id - }; - - this.LanguageRepositoryMock.Setup(p => p.DoesLanguageNameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - this.LanguageRepositoryMock.Setup(p => p.AddAsync(It.IsAny<Language>())).Returns(Task.FromResult(true)); - this.LanguageRepositoryMock.Setup(p => p.GetByNameAsync(It.IsAny<string>())).Returns(Task.FromResult(language)); - this.MapperMock.Setup(p => p.Map<Language>(It.IsAny<CreateLanguageServiceModel>())).Returns(language); - - Guid result = await this.LanguageService.CreateLanguage(createLanguageServiceModel); - - Assert.AreEqual(id, result); - } - - [Test] - public async Task CreateLanguage_ReturnsEmptyGuid_WhenEntityIsNotAddedSuccessfully() - { - string languageName = "Gosho Trapov"; - - CreateLanguageServiceModel createLanguageServiceModel = new CreateLanguageServiceModel - { - Name = languageName - }; - Language language = new Language - { - Name = languageName - }; - - this.LanguageRepositoryMock.Setup(p => p.DoesLanguageNameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - this.LanguageRepositoryMock.Setup(p => p.AddAsync(It.IsAny<Language>())).Returns(Task.FromResult(false)); - this.MapperMock.Setup(p => p.Map<Language>(It.IsAny<CreateLanguageServiceModel>())).Returns(language); - - Guid result = await this.LanguageService.CreateLanguage(createLanguageServiceModel); - - Assert.IsTrue(result == Guid.Empty); - - } - - [Test] - public void CreateLanguage_ThrowsArgumentException_WhenEntityAlreadyExists() - { - string exceptionMessage = "Language already exists!"; - string languageName = "Gosho Trapov"; - - CreateLanguageServiceModel createLanguageServiceModel = new CreateLanguageServiceModel - { - Name = languageName - }; - Language language = new Language - { - Name = languageName - }; - - this.LanguageRepositoryMock.Setup(p => p.DoesLanguageNameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(true)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.LanguageService.CreateLanguage(createLanguageServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region GetLanguageById - [Test] - public async Task GetLanguageById_ReturnsTheLanguage_WhenItExists() - { - Guid id = new Guid(); - string name = "Gosho Trapov"; - Language language = new Language - { - Name = name - }; - ReadLanguageServiceModel readLanguageServiceModel = new ReadLanguageServiceModel - { - Name = name - }; - - this.LanguageRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(language)); - this.MapperMock.Setup(p => p.Map<ReadLanguageServiceModel>(It.IsAny<Language>())).Returns(readLanguageServiceModel); - - ReadLanguageServiceModel result = await this.LanguageService.GetLanguageById(id); - - Assert.AreEqual(name, result.Name); - } - - [Test] - public void GetLanguageById_ThrowsException_WhenLanguageDoesNotExist() - { - string exceptionMessage = "The language does not exist"; - Guid id = new Guid(); - this.LanguageRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult<Language>(null)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.LanguageService.GetLanguageById(id)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region GetLanguages - [Test] - public void GetLanguages_ReturnsAllLanguages_IfAnyExist() - { - ReadLanguageServiceModel firstLanguage = new ReadLanguageServiceModel(); - ReadLanguageServiceModel secondLanguage = new ReadLanguageServiceModel(); - HashSet<ReadLanguageServiceModel> languges = new HashSet<ReadLanguageServiceModel>(); - languges.Add(firstLanguage); - languges.Add(secondLanguage); - - this.LanguageRepositoryMock.Setup(p => p.GetLanguages()).Returns(new HashSet<Language>()); - this.MapperMock.Setup(p => p.Map<HashSet<ReadLanguageServiceModel>>(It.IsAny<HashSet<Language>>())).Returns(languges); - - HashSet<ReadLanguageServiceModel> result = this.LanguageService.GetLanguages(); - - Assert.GreaterOrEqual(2, result.Count, "GetLanguages does not return all languages"); - } - - [Test] - public void GetLanguages_ReturnsEmptyHashSet_IfNoLanguagesExist() - { - this.LanguageRepositoryMock.Setup(p => p.GetLanguages()).Returns(new HashSet<Language>()); - this.MapperMock.Setup(p => p.Map<HashSet<ReadLanguageServiceModel>>(It.IsAny<HashSet<Language>>())).Returns(new HashSet<ReadLanguageServiceModel>()); - - HashSet<ReadLanguageServiceModel> result = this.LanguageService.GetLanguages(); - - Assert.IsEmpty(result, "GetLanguages does not return empty string when no languages exist"); - } - #endregion - - #region UpdateLanguage - [Test] - [TestCase(true)] - [TestCase(false)] - public async Task UpdateLanguage_ReturnsIfUpdateIsSuccessfull_WhenLanguageExistsy(bool shouldPass) - { - string name = "Gosho Trapov"; - Guid id = Guid.NewGuid(); - Language language = new Language - { - Name = name, - Id = id - }; - UpdateLanguageServiceModel updateLanguageServiceModel = new UpdateLanguageServiceModel - { - Name = name, - }; - - this.LanguageRepositoryMock.Setup(p => p.DoesLanguageExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.LanguageRepositoryMock.Setup(p => p.DoesLanguageNameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - this.LanguageRepositoryMock.Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Language>())).Returns(Task.FromResult(shouldPass)); - this.MapperMock.Setup(p => p.Map<Language>(It.IsAny<UpdateLanguageServiceModel>())).Returns(language); - - bool result = await this.LanguageService.UpdateLanguage(updateLanguageServiceModel); - - Assert.AreEqual(shouldPass, result); - } - - [Test] - public void UpdateLanguage_ThrowsArgumentException_WhenLanguageDoesNotExist() - { - string exceptionMessage = "Language does not exist!"; - UpdateLanguageServiceModel updateTechnologyServiceModel = new UpdateLanguageServiceModel - { - }; - - this.LanguageRepositoryMock.Setup(p => p.DoesLanguageExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.LanguageService.UpdateLanguage(updateTechnologyServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - - [Test] - public void UpdateLanguage_ThrowsException_WhenLanguageNameAlreadyExists() - { - string exceptionMessage = "Language name already exists in our data base!"; - UpdateLanguageServiceModel updateTechnologyServiceModel = new UpdateLanguageServiceModel - { - }; - - this.LanguageRepositoryMock.Setup(p => p.DoesLanguageExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.LanguageRepositoryMock.Setup(p => p.DoesLanguageNameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(true)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.LanguageService.UpdateLanguage(updateTechnologyServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region DeleteLanguage - [Test] - [TestCase(true)] - [TestCase(false)] - public async Task DeleteLanguage_ShouldReturnIfDeletionIsSuccessfull_WhenLanguageExists(bool shouldPass) - { - Guid id = new Guid(); - Language language = new Language(); - - this.LanguageRepositoryMock.Setup(p => p.DoesLanguageExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.LanguageRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(language)); - this.LanguageRepositoryMock.Setup(p => p.DeleteAsync(It.IsAny<Language>())).Returns(Task.FromResult(shouldPass)); - - bool result = await this.LanguageService.DeleteLanguage(id); - - Assert.AreEqual(shouldPass, result); - } - - [Test] - public void DeleteLanguage_ThrowsException_WhenLanguageDoesNotExist() - { - string exceptionMessage = "Language does not exist!"; - Guid id = new Guid(); - - this.LanguageRepositoryMock.Setup(p => p.DoesLanguageExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.LanguageService.DeleteLanguage(id)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Services.Tests/PostService.Tests.cs b/src/DevHive.Tests/DevHive.Services.Tests/PostService.Tests.cs deleted file mode 100644 index 900608c..0000000 --- a/src/DevHive.Tests/DevHive.Services.Tests/PostService.Tests.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Data.Interfaces.Repositories; -using DevHive.Data.Models; -using DevHive.Services.Interfaces; -using DevHive.Services.Models.Post; -using DevHive.Services.Services; -using Microsoft.AspNetCore.Http; -using Moq; -using NUnit.Framework; - -namespace DevHive.Services.Tests -{ - [TestFixture] - public class PostServiceTests - { - private const string MESSAGE = "Gosho Trapov"; - private Mock<ICloudService> CloudServiceMock { get; set; } - private Mock<IPostRepository> PostRepositoryMock { get; set; } - private Mock<ICommentRepository> CommentRepositoryMock { get; set; } - private Mock<IUserRepository> UserRepositoryMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private PostService PostService { get; set; } - - #region SetUps - [SetUp] - public void Setup() - { - this.PostRepositoryMock = new Mock<IPostRepository>(); - this.CloudServiceMock = new Mock<ICloudService>(); - this.UserRepositoryMock = new Mock<IUserRepository>(); - this.CommentRepositoryMock = new Mock<ICommentRepository>(); - this.MapperMock = new Mock<IMapper>(); - this.PostService = new PostService(this.CloudServiceMock.Object, this.UserRepositoryMock.Object, this.PostRepositoryMock.Object, this.CommentRepositoryMock.Object, this.MapperMock.Object); - } - #endregion - - #region CreatePost - [Test] - public async Task CreatePost_ReturnsIdOfThePost_WhenItIsSuccessfullyCreated() - { - Guid postId = Guid.NewGuid(); - User creator = new User { Id = Guid.NewGuid() }; - CreatePostServiceModel createPostServiceModel = new CreatePostServiceModel - { - Files = new List<IFormFile>() - }; - Post post = new Post - { - Message = MESSAGE, - Id = postId, - }; - - this.PostRepositoryMock.Setup(p => p.AddAsync(It.IsAny<Post>())).Returns(Task.FromResult(true)); - this.PostRepositoryMock.Setup(p => p.GetPostByCreatorAndTimeCreatedAsync(It.IsAny<Guid>(), It.IsAny<DateTime>())).Returns(Task.FromResult(post)); - this.UserRepositoryMock.Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.UserRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(creator)); - this.MapperMock.Setup(p => p.Map<Post>(It.IsAny<CreatePostServiceModel>())).Returns(post); - - Guid result = await this.PostService.CreatePost(createPostServiceModel); - - Assert.AreEqual(postId, result, "CreatePost does not return the correct id"); - } - - [Test] - public async Task CreatePost_ReturnsEmptyGuid_WhenItIsNotSuccessfullyCreated() - { - CreatePostServiceModel createPostServiceModel = new CreatePostServiceModel - { - Files = new List<IFormFile>() - }; - Post post = new Post - { - Message = MESSAGE, - }; - - this.PostRepositoryMock.Setup(p => p.AddAsync(It.IsAny<Post>())).Returns(Task.FromResult(false)); - this.UserRepositoryMock.Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.MapperMock.Setup(p => p.Map<Post>(It.IsAny<CreatePostServiceModel>())).Returns(post); - - Guid result = await this.PostService.CreatePost(createPostServiceModel); - - Assert.AreEqual(Guid.Empty, result, "CreatePost does not return empty id"); - } - - [Test] - public void CreatePost_ThrowsException_WhenUserDoesNotExist() - { - const string EXCEPTION_MESSAGE = "User does not exist!"; - CreatePostServiceModel createPostServiceModel = new CreatePostServiceModel - { - }; - Post post = new Post - { - Message = MESSAGE, - }; - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.PostService.CreatePost(createPostServiceModel), "CreatePost does not throw excpeion when the user does not exist"); - - Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Excapetion message is not correct"); - } - #endregion - - #region GetPostById - [Test] - public async Task GetPostById_ReturnsThePost_WhenItExists() - { - Guid creatorId = new Guid(); - User creator = new User { Id = creatorId }; - Post post = new Post - { - Message = MESSAGE, - Creator = creator - }; - ReadPostServiceModel readPostServiceModel = new ReadPostServiceModel - { - Message = MESSAGE - }; - User user = new User - { - Id = creatorId, - }; - - this.PostRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(post)); - this.UserRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(user)); - this.MapperMock.Setup(p => p.Map<ReadPostServiceModel>(It.IsAny<Post>())).Returns(readPostServiceModel); - - ReadPostServiceModel result = await this.PostService.GetPostById(new Guid()); - - Assert.AreEqual(MESSAGE, result.Message); - } - - [Test] - public void GetPostById_ThorwsException_WhenTheUserDoesNotExist() - { - const string EXCEPTION_MESSAGE = "The user does not exist!"; - Guid creatorId = new Guid(); - User creator = new User { Id = creatorId }; - Post post = new Post - { - Message = MESSAGE, - Creator = creator - }; - - this.PostRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(post)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.PostService.GetPostById(new Guid()), "GetPostById does not throw exception when the user does not exist"); - - Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message); - } - - [Test] - public void GetPostById_ThrowsException_WhenCommentDoesNotExist() - { - string exceptionMessage = "The post does not exist!"; - Guid creatorId = new Guid(); - User user = new User - { - Id = creatorId, - }; - - this.PostRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult<Post>(null)); - this.UserRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(user)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.PostService.GetPostById(new Guid())); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region UpdatePost - [Test] - public async Task UpdatePost_ReturnsTheIdOfThePost_WhenUpdatedSuccessfully() - { - Guid id = Guid.NewGuid(); - Post post = new Post - { - Id = id, - Message = MESSAGE - }; - UpdatePostServiceModel updatePostServiceModel = new UpdatePostServiceModel - { - PostId = id, - NewMessage = MESSAGE, - Files = new List<IFormFile>() - }; - - this.PostRepositoryMock.Setup(p => p.DoesPostExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.PostRepositoryMock.Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Post>())).Returns(Task.FromResult(true)); - this.PostRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(post)); - this.MapperMock.Setup(p => p.Map<Post>(It.IsAny<UpdatePostServiceModel>())).Returns(post); - - Guid result = await this.PostService.UpdatePost(updatePostServiceModel); - - Assert.AreEqual(updatePostServiceModel.PostId, result); - } - - [Test] - public async Task UpdatePost_ReturnsEmptyId_WhenThePostIsNotUpdatedSuccessfully() - { - Post post = new Post - { - Message = MESSAGE - }; - UpdatePostServiceModel updatePostServiceModel = new UpdatePostServiceModel - { - PostId = Guid.NewGuid(), - NewMessage = MESSAGE, - Files = new List<IFormFile>() - }; - - this.PostRepositoryMock.Setup(p => p.DoesPostExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.PostRepositoryMock.Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Post>())).Returns(Task.FromResult(false)); - this.MapperMock.Setup(p => p.Map<Post>(It.IsAny<UpdatePostServiceModel>())).Returns(post); - - Guid result = await this.PostService.UpdatePost(updatePostServiceModel); - - Assert.AreEqual(Guid.Empty, result); - } - - [Test] - public void UpdatePost_ThrowsArgumentException_WhenCommentDoesNotExist() - { - string exceptionMessage = "Post does not exist!"; - UpdatePostServiceModel updatePostServiceModel = new UpdatePostServiceModel - { - }; - - this.PostRepositoryMock.Setup(p => p.DoesPostExist(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.PostService.UpdatePost(updatePostServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region DeletePost - [Test] - [TestCase(true)] - [TestCase(false)] - public async Task Deletepost_ShouldReturnIfDeletionIsSuccessfull_WhenPostExists(bool shouldPass) - { - Guid id = new Guid(); - Post post = new Post(); - - this.PostRepositoryMock.Setup(p => p.DoesPostExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.PostRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(post)); - this.PostRepositoryMock.Setup(p => p.DeleteAsync(It.IsAny<Post>())).Returns(Task.FromResult(shouldPass)); - - bool result = await this.PostService.DeletePost(id); - - Assert.AreEqual(shouldPass, result); - } - - [Test] - public void DeletePost_ThrowsException_WhenPostDoesNotExist() - { - string exceptionMessage = "Post does not exist!"; - Guid id = new Guid(); - - this.PostRepositoryMock.Setup(p => p.DoesPostExist(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.PostService.DeletePost(id)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Services.Tests/RoleService.Tests.cs b/src/DevHive.Tests/DevHive.Services.Tests/RoleService.Tests.cs deleted file mode 100644 index e500dd1..0000000 --- a/src/DevHive.Tests/DevHive.Services.Tests/RoleService.Tests.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Data.Interfaces.Repositories; -using DevHive.Data.Models; -using DevHive.Services.Models.Identity.Role; -using DevHive.Services.Services; -using Moq; -using NUnit.Framework; - -namespace DevHive.Services.Tests -{ - [TestFixture] - public class RoleServiceTests - { - private Mock<IRoleRepository> RoleRepositoryMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private RoleService RoleService { get; set; } - - #region SetUps - [SetUp] - public void Setup() - { - this.RoleRepositoryMock = new Mock<IRoleRepository>(); - this.MapperMock = new Mock<IMapper>(); - this.RoleService = new RoleService(this.RoleRepositoryMock.Object, this.MapperMock.Object); - } - #endregion - - #region CreateRole - [Test] - public async Task CreateRole_ReturnsNonEmptyGuid_WhenEntityIsAddedSuccessfully() - { - string roleName = "Gosho Trapov"; - Guid id = Guid.NewGuid(); - CreateRoleServiceModel createRoleServiceModel = new CreateRoleServiceModel - { - Name = roleName - }; - Role role = new() - { - Name = roleName, - Id = id - }; - - this.RoleRepositoryMock.Setup(p => p.DoesNameExist(It.IsAny<string>())).Returns(Task.FromResult(false)); - this.RoleRepositoryMock.Setup(p => p.AddAsync(It.IsAny<Role>())).Returns(Task.FromResult(true)); - this.RoleRepositoryMock.Setup(p => p.GetByNameAsync(It.IsAny<string>())).Returns(Task.FromResult(role)); - this.MapperMock.Setup(p => p.Map<Role>(It.IsAny<CreateRoleServiceModel>())).Returns(role); - - Guid result = await this.RoleService.CreateRole(createRoleServiceModel); - - Assert.AreEqual(id, result); - } - - [Test] - public async Task CreateRoley_ReturnsEmptyGuid_WhenEntityIsNotAddedSuccessfully() - { - string roleName = "Gosho Trapov"; - - CreateRoleServiceModel createRoleServiceModel = new CreateRoleServiceModel - { - Name = roleName - }; - Role role = new Role - { - Name = roleName - }; - - this.RoleRepositoryMock.Setup(p => p.DoesNameExist(It.IsAny<string>())).Returns(Task.FromResult(false)); - this.RoleRepositoryMock.Setup(p => p.AddAsync(It.IsAny<Role>())).Returns(Task.FromResult(false)); - this.MapperMock.Setup(p => p.Map<Role>(It.IsAny<CreateRoleServiceModel>())).Returns(role); - - Guid result = await this.RoleService.CreateRole(createRoleServiceModel); - - Assert.IsTrue(result == Guid.Empty); - } - - [Test] - public void CreateTechnology_ThrowsArgumentException_WhenEntityAlreadyExists() - { - string exceptionMessage = "Role already exists!"; - string roleName = "Gosho Trapov"; - - CreateRoleServiceModel createRoleServiceModel = new CreateRoleServiceModel - { - Name = roleName - }; - Role role = new Role - { - Name = roleName - }; - - this.RoleRepositoryMock.Setup(p => p.DoesNameExist(It.IsAny<string>())).Returns(Task.FromResult(true)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.RoleService.CreateRole(createRoleServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region GetRoleById - [Test] - public async Task GetRoleById_ReturnsTheRole_WhenItExists() - { - Guid id = new Guid(); - string name = "Gosho Trapov"; - Role role = new Role - { - Name = name - }; - RoleServiceModel roleServiceModel = new RoleServiceModel - { - Name = name - }; - - this.RoleRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(role)); - this.MapperMock.Setup(p => p.Map<RoleServiceModel>(It.IsAny<Role>())).Returns(roleServiceModel); - - RoleServiceModel result = await this.RoleService.GetRoleById(id); - - Assert.AreEqual(name, result.Name); - } - - [Test] - public void GetRoleById_ThrowsException_WhenRoleDoesNotExist() - { - string exceptionMessage = "Role does not exist!"; - Guid id = new Guid(); - this.RoleRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult<Role>(null)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.RoleService.GetRoleById(id)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region UpdateRole - [Test] - [TestCase(true)] - [TestCase(false)] - public async Task UpdateRole_ReturnsIfUpdateIsSuccessfull_WhenRoleExistsy(bool shouldPass) - { - string name = "Gosho Trapov"; - Guid id = Guid.NewGuid(); - Role role = new Role - { - Name = name, - Id = id - }; - UpdateRoleServiceModel updateRoleServiceModel = new UpdateRoleServiceModel - { - Name = name, - }; - - this.RoleRepositoryMock.Setup(p => p.DoesRoleExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.RoleRepositoryMock.Setup(p => p.DoesNameExist(It.IsAny<string>())).Returns(Task.FromResult(false)); - this.RoleRepositoryMock.Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Role>())).Returns(Task.FromResult(shouldPass)); - this.MapperMock.Setup(p => p.Map<Role>(It.IsAny<UpdateRoleServiceModel>())).Returns(role); - - bool result = await this.RoleService.UpdateRole(updateRoleServiceModel); - - Assert.AreEqual(shouldPass, result); - } - - [Test] - public void UpdateRole_ThrowsException_WhenRoleDoesNotExist() - { - string exceptionMessage = "Role does not exist!"; - UpdateRoleServiceModel updateRoleServiceModel = new UpdateRoleServiceModel - { - }; - - this.RoleRepositoryMock.Setup(p => p.DoesRoleExist(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.RoleService.UpdateRole(updateRoleServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - - [Test] - public void UpdateRole_ThrowsException_WhenRoleNameAlreadyExists() - { - string exceptionMessage = "Role name already exists!"; - UpdateRoleServiceModel updateRoleServiceModel = new UpdateRoleServiceModel - { - }; - - this.RoleRepositoryMock.Setup(p => p.DoesRoleExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.RoleRepositoryMock.Setup(p => p.DoesNameExist(It.IsAny<string>())).Returns(Task.FromResult(true)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.RoleService.UpdateRole(updateRoleServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region DeleteRole - [Test] - [TestCase(true)] - [TestCase(false)] - public async Task DeleteRole_ShouldReturnIfDeletionIsSuccessfull_WhenRoleExists(bool shouldPass) - { - Guid id = new Guid(); - Role role = new Role(); - - this.RoleRepositoryMock.Setup(p => p.DoesRoleExist(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.RoleRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(role)); - this.RoleRepositoryMock.Setup(p => p.DeleteAsync(It.IsAny<Role>())).Returns(Task.FromResult(shouldPass)); - - bool result = await this.RoleService.DeleteRole(id); - - Assert.AreEqual(shouldPass, result); - } - - [Test] - public void DeleteRole_ThrowsException_WhenRoleDoesNotExist() - { - string exceptionMessage = "Role does not exist!"; - Guid id = new Guid(); - - this.RoleRepositoryMock.Setup(p => p.DoesRoleExist(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.RoleService.DeleteRole(id)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Services.Tests/TechnologyServices.Tests.cs b/src/DevHive.Tests/DevHive.Services.Tests/TechnologyServices.Tests.cs deleted file mode 100644 index e671adb..0000000 --- a/src/DevHive.Tests/DevHive.Services.Tests/TechnologyServices.Tests.cs +++ /dev/null @@ -1,262 +0,0 @@ -using AutoMapper; -using DevHive.Data.Interfaces.Repositories; -using DevHive.Data.Models; -using DevHive.Services.Models.Technology; -using DevHive.Services.Services; -using Moq; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace DevHive.Services.Tests -{ - [TestFixture] - public class TechnologyServicesTests - { - private Mock<ITechnologyRepository> TechnologyRepositoryMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private TechnologyService TechnologyService { get; set; } - - #region SetUps - [SetUp] - public void Setup() - { - this.TechnologyRepositoryMock = new Mock<ITechnologyRepository>(); - this.MapperMock = new Mock<IMapper>(); - this.TechnologyService = new TechnologyService(this.TechnologyRepositoryMock.Object, this.MapperMock.Object); - } - #endregion - - #region CreateTechnology - [Test] - public async Task CreateTechnology_ReturnsNonEmptyGuid_WhenEntityIsAddedSuccessfully() - { - string technologyName = "Gosho Trapov"; - Guid id = Guid.NewGuid(); - CreateTechnologyServiceModel createTechnologyServiceModel = new() - { - Name = technologyName - }; - Technology technology = new() - { - Name = technologyName, - Id = id - }; - - this.TechnologyRepositoryMock.Setup(p => p.DoesTechnologyNameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - this.TechnologyRepositoryMock.Setup(p => p.AddAsync(It.IsAny<Technology>())).Returns(Task.FromResult(true)); - this.TechnologyRepositoryMock.Setup(p => p.GetByNameAsync(It.IsAny<string>())).Returns(Task.FromResult(technology)); - this.MapperMock.Setup(p => p.Map<Technology>(It.IsAny<CreateTechnologyServiceModel>())).Returns(technology); - - Guid result = await this.TechnologyService.CreateTechnology(createTechnologyServiceModel); - - Assert.AreEqual(id, result); - } - - [Test] - public async Task CreateTechnology_ReturnsEmptyGuid_WhenEntityIsNotAddedSuccessfully() - { - string technologyName = "Gosho Trapov"; - - CreateTechnologyServiceModel createTechnologyServiceModel = new() - { - Name = technologyName - }; - Technology technology = new() - { - Name = technologyName - }; - - this.TechnologyRepositoryMock.Setup(p => p.DoesTechnologyNameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - this.TechnologyRepositoryMock.Setup(p => p.AddAsync(It.IsAny<Technology>())).Returns(Task.FromResult(false)); - this.MapperMock.Setup(p => p.Map<Technology>(It.IsAny<CreateTechnologyServiceModel>())).Returns(technology); - - Guid result = await this.TechnologyService.CreateTechnology(createTechnologyServiceModel); - - Assert.IsTrue(result == Guid.Empty); - } - - [Test] - public void CreateTechnology_ThrowsArgumentException_WhenEntityAlreadyExists() - { - string exceptionMessage = "Technology already exists!"; - string technologyName = "Gosho Trapov"; - - CreateTechnologyServiceModel createTechnologyServiceModel = new() - { - Name = technologyName - }; - Technology technology = new() - { - Name = technologyName - }; - - this.TechnologyRepositoryMock.Setup(p => p.DoesTechnologyNameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(true)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.TechnologyService.CreateTechnology(createTechnologyServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region GetTechnologyById - [Test] - public async Task GetTechnologyById_ReturnsTheTechnology_WhenItExists() - { - Guid id = new Guid(); - string name = "Gosho Trapov"; - Technology technology = new() - { - Name = name - }; - ReadTechnologyServiceModel readTechnologyServiceModel = new() - { - Name = name - }; - - this.TechnologyRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(technology)); - this.MapperMock.Setup(p => p.Map<ReadTechnologyServiceModel>(It.IsAny<Technology>())).Returns(readTechnologyServiceModel); - - ReadTechnologyServiceModel result = await this.TechnologyService.GetTechnologyById(id); - - Assert.AreEqual(name, result.Name); - } - - [Test] - public void GetTechnologyById_ThrowsException_WhenTechnologyDoesNotExist() - { - string exceptionMessage = "The technology does not exist"; - Guid id = new Guid(); - this.TechnologyRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult<Technology>(null)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.TechnologyService.GetTechnologyById(id)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region GetTechnologies - [Test] - public void GetTechnologies_ReturnsAllLanguages_IfAnyExist() - { - ReadTechnologyServiceModel firstTechnology = new ReadTechnologyServiceModel(); - ReadTechnologyServiceModel secondTechnology = new ReadTechnologyServiceModel(); - HashSet<ReadTechnologyServiceModel> technologies = new HashSet<ReadTechnologyServiceModel>(); - technologies.Add(firstTechnology); - technologies.Add(secondTechnology); - - this.TechnologyRepositoryMock.Setup(p => p.GetTechnologies()).Returns(new HashSet<Technology>()); - this.MapperMock.Setup(p => p.Map<HashSet<ReadTechnologyServiceModel>>(It.IsAny<HashSet<Technology>>())).Returns(technologies); - - HashSet<ReadTechnologyServiceModel> result = this.TechnologyService.GetTechnologies(); - - Assert.GreaterOrEqual(2, result.Count, "GetTechnologies does not return all technologies"); - } - - [Test] - public void GetLanguages_ReturnsEmptyHashSet_IfNoLanguagesExist() - { - this.TechnologyRepositoryMock.Setup(p => p.GetTechnologies()).Returns(new HashSet<Technology>()); - this.MapperMock.Setup(p => p.Map<HashSet<ReadTechnologyServiceModel>>(It.IsAny<HashSet<Technology>>())).Returns(new HashSet<ReadTechnologyServiceModel>()); - - HashSet<ReadTechnologyServiceModel> result = this.TechnologyService.GetTechnologies(); - - Assert.IsEmpty(result, "GetTechnologies does not return empty string when no technologies exist"); - } - #endregion - - #region UpdateTechnology - [Test] - [TestCase(true)] - [TestCase(false)] - public async Task UpdateTechnology_ReturnsIfUpdateIsSuccessfull_WhenTechnologyExistsy(bool shouldPass) - { - string name = "Gosho Trapov"; - Guid id = Guid.NewGuid(); - Technology technology = new Technology - { - Name = name, - Id = id - }; - UpdateTechnologyServiceModel updatetechnologyServiceModel = new UpdateTechnologyServiceModel - { - Name = name, - }; - - this.TechnologyRepositoryMock.Setup(p => p.DoesTechnologyExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.TechnologyRepositoryMock.Setup(p => p.DoesTechnologyNameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - this.TechnologyRepositoryMock.Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Technology>())).Returns(Task.FromResult(shouldPass)); - this.MapperMock.Setup(p => p.Map<Technology>(It.IsAny<UpdateTechnologyServiceModel>())).Returns(technology); - - bool result = await this.TechnologyService.UpdateTechnology(updatetechnologyServiceModel); - - Assert.AreEqual(shouldPass, result); - } - - [Test] - public void UpdateTechnology_ThrowsException_WhenTechnologyDoesNotExist() - { - string exceptionMessage = "Technology does not exist!"; - UpdateTechnologyServiceModel updateTechnologyServiceModel = new UpdateTechnologyServiceModel - { - }; - - this.TechnologyRepositoryMock.Setup(p => p.DoesTechnologyExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.TechnologyService.UpdateTechnology(updateTechnologyServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - - [Test] - public void UpdateTechnology_ThrowsException_WhenTechnologyNameAlreadyExists() - { - string exceptionMessage = "Technology name already exists!"; - UpdateTechnologyServiceModel updateTechnologyServiceModel = new UpdateTechnologyServiceModel - { - }; - - this.TechnologyRepositoryMock.Setup(p => p.DoesTechnologyExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.TechnologyRepositoryMock.Setup(p => p.DoesTechnologyNameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(true)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.TechnologyService.UpdateTechnology(updateTechnologyServiceModel)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region DeleteTechnology - - [Test] - [TestCase(true)] - [TestCase(false)] - public async Task DeleteTechnology_ShouldReturnIfDeletionIsSuccessfull_WhenTechnologyExists(bool shouldPass) - { - Guid id = new Guid(); - Technology technology = new Technology(); - - this.TechnologyRepositoryMock.Setup(p => p.DoesTechnologyExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.TechnologyRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(technology)); - this.TechnologyRepositoryMock.Setup(p => p.DeleteAsync(It.IsAny<Technology>())).Returns(Task.FromResult(shouldPass)); - - bool result = await this.TechnologyService.DeleteTechnology(id); - - Assert.AreEqual(shouldPass, result); - } - - [Test] - public void DeleteTechnology_ThrowsException_WhenTechnologyDoesNotExist() - { - string exceptionMessage = "Technology does not exist!"; - Guid id = new Guid(); - - this.TechnologyRepositoryMock.Setup(p => p.DoesTechnologyExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.TechnologyService.DeleteTechnology(id)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Services.Tests/UserService.Tests.cs b/src/DevHive.Tests/DevHive.Services.Tests/UserService.Tests.cs deleted file mode 100644 index 21d4862..0000000 --- a/src/DevHive.Tests/DevHive.Services.Tests/UserService.Tests.cs +++ /dev/null @@ -1,394 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; -using System.Text; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Common.Models.Identity; -using DevHive.Common.Models.Misc; -using DevHive.Data.Interfaces.Repositories; -using DevHive.Data.Models; -using DevHive.Services.Interfaces; -using DevHive.Services.Models.Identity.User; -using DevHive.Services.Options; -using DevHive.Services.Services; -using Microsoft.AspNetCore.Identity; -using Microsoft.IdentityModel.Tokens; -using Moq; -using NUnit.Framework; - -namespace DevHive.Services.Tests -{ - [TestFixture] - public class UserServiceTests - { - private Mock<ICloudService> CloudServiceMock { get; set; } - private Mock<IUserRepository> UserRepositoryMock { get; set; } - private Mock<IRoleRepository> RoleRepositoryMock { get; set; } - private Mock<ILanguageRepository> LanguageRepositoryMock { get; set; } - private Mock<ITechnologyRepository> TechnologyRepositoryMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private JWTOptions JWTOptions { get; set; } - private UserService UserService { get; set; } - - #region SetUps - [SetUp] - public void Setup() - { - this.UserRepositoryMock = new Mock<IUserRepository>(); - this.RoleRepositoryMock = new Mock<IRoleRepository>(); - this.CloudServiceMock = new Mock<ICloudService>(); - this.LanguageRepositoryMock = new Mock<ILanguageRepository>(); - this.TechnologyRepositoryMock = new Mock<ITechnologyRepository>(); - this.JWTOptions = new JWTOptions("gXfQlU6qpDleFWyimscjYcT3tgFsQg3yoFjcvSLxG56n1Vu2yptdIUq254wlJWjm"); - this.MapperMock = new Mock<IMapper>(); - // TODO: give actual UserManager and RoleManager to UserService - this.UserService = new UserService(this.UserRepositoryMock.Object, this.LanguageRepositoryMock.Object, this.RoleRepositoryMock.Object, this.TechnologyRepositoryMock.Object, null, null, this.MapperMock.Object, this.JWTOptions, this.CloudServiceMock.Object); - } - #endregion - - #region LoginUser - [Test] - public async Task LoginUser_ReturnsTokenModel_WhenLoggingUserIn() - { - string somePassword = "GoshoTrapovImaGolemChep"; - const string name = "GoshoTrapov"; - string hashedPassword = PasswordModifications.GeneratePasswordHash(somePassword); - LoginServiceModel loginServiceModel = new LoginServiceModel - { - Password = somePassword - }; - User user = new User - { - Id = Guid.NewGuid(), - PasswordHash = hashedPassword, - UserName = name - }; - - this.UserRepositoryMock.Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(true)); - this.UserRepositoryMock.Setup(p => p.GetByUsernameAsync(It.IsAny<string>())).Returns(Task.FromResult(user)); - - string JWTSecurityToken = this.WriteJWTSecurityToken(user.Id, user.UserName, user.Roles); - - TokenModel tokenModel = await this.UserService.LoginUser(loginServiceModel); - - Assert.AreEqual(JWTSecurityToken, tokenModel.Token, "LoginUser does not return the correct token"); - } - - [Test] - public void LoginUser_ThrowsException_WhenUserNameDoesNotExist() - { - const string EXCEPTION_MESSAGE = "Invalid username!"; - LoginServiceModel loginServiceModel = new LoginServiceModel - { - }; - - this.UserRepositoryMock.Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.UserService.LoginUser(loginServiceModel)); - - Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorect Exception message"); - } - - [Test] - public void LoginUser_ThroiwsException_WhenPasswordIsIncorect() - { - const string EXCEPTION_MESSAGE = "Incorrect password!"; - string somePassword = "GoshoTrapovImaGolemChep"; - LoginServiceModel loginServiceModel = new LoginServiceModel - { - Password = somePassword - }; - User user = new User - { - Id = Guid.NewGuid(), - PasswordHash = "InvalidPasswordHas" - }; - - this.UserRepositoryMock.Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(true)); - this.UserRepositoryMock.Setup(p => p.GetByUsernameAsync(It.IsAny<string>())).Returns(Task.FromResult(user)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.UserService.LoginUser(loginServiceModel)); - - Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorect Exception message"); - } - #endregion - - #region RegisterUser - // [Test] - // public async Task RegisterUser_ReturnsTokenModel_WhenUserIsSuccessfull() - // { - // string somePassword = "GoshoTrapovImaGolemChep"; - // const string name = "GoshoTrapov"; - // RegisterServiceModel registerServiceModel = new RegisterServiceModel - // { - // Password = somePassword - // }; - // User user = new User - // { - // Id = Guid.NewGuid(), - // UserName = name - // }; - // Role role = new Role { Name = Role.DefaultRole }; - // HashSet<Role> roles = new HashSet<Role> { role }; - // - // this.UserRepositoryMock.Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - // this.UserRepositoryMock.Setup(p => p.DoesEmailExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - // this.RoleRepositoryMock.Setup(p => p.DoesNameExist(It.IsAny<string>())).Returns(Task.FromResult(true)); - // this.RoleRepositoryMock.Setup(p => p.GetByNameAsync(It.IsAny<string>())).Returns(Task.FromResult(role)); - // this.MapperMock.Setup(p => p.Map<User>(It.IsAny<RegisterServiceModel>())).Returns(user); - // this.UserRepositoryMock.Setup(p => p.AddAsync(It.IsAny<User>())).Verifiable(); - // - // string JWTSecurityToken = this.WriteJWTSecurityToken(user.Id, user.UserName, roles); - // - // TokenModel tokenModel = await this.UserService.RegisterUser(registerServiceModel); - // - // Mock.Verify(); - // Assert.AreEqual(JWTSecurityToken, tokenModel.Token, "RegisterUser does not return the correct token"); - // } - - [Test] - public void RegisterUser_ThrowsException_WhenUsernameAlreadyExists() - { - const string EXCEPTION_MESSAGE = "Username already exists!"; - RegisterServiceModel registerServiceModel = new RegisterServiceModel - { - }; - - this.UserRepositoryMock.Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(true)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.UserService.RegisterUser(registerServiceModel)); - - Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorect Exception message"); - } - - [Test] - public void RegisterUser_ThrowsException_WhenEmailAlreadyExists() - { - const string EXCEPTION_MESSAGE = "Email already exists!"; - - RegisterServiceModel registerServiceModel = new RegisterServiceModel - { - }; - - this.UserRepositoryMock.Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - this.UserRepositoryMock.Setup(p => p.DoesEmailExistAsync(It.IsAny<string>())).Returns(Task.FromResult(true)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.UserService.RegisterUser(registerServiceModel)); - - Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorect Exception message"); - } - #endregion - - #region GetUserById - [Test] - public async Task GetUserById_ReturnsTheUser_WhenItExists() - { - Guid id = new Guid(); - string name = "Gosho Trapov"; - User user = new() - { - }; - UserServiceModel userServiceModel = new UserServiceModel - { - UserName = name - }; - - this.UserRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(user)); - this.MapperMock.Setup(p => p.Map<UserServiceModel>(It.IsAny<User>())).Returns(userServiceModel); - - UserServiceModel result = await this.UserService.GetUserById(id); - - Assert.AreEqual(name, result.UserName); - } - - [Test] - public void GetTechnologyById_ThrowsException_WhenTechnologyDoesNotExist() - { - const string EXCEPTION_MESSEGE = "User does not exist!"; - Guid id = new Guid(); - this.UserRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult<User>(null)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.UserService.GetUserById(id)); - - Assert.AreEqual(EXCEPTION_MESSEGE, ex.Message, "Incorecct exception message"); - } - #endregion - - #region GetUserByUsername - [Test] - public async Task GetUserByUsername_ReturnsTheCorrectUser_IfItExists() - { - string username = "Gosho Trapov"; - User user = new User(); - UserServiceModel userServiceModel = new UserServiceModel - { - UserName = username - }; - - this.UserRepositoryMock.Setup(p => p.GetByUsernameAsync(It.IsAny<string>())).Returns(Task.FromResult(user)); - this.MapperMock.Setup(p => p.Map<UserServiceModel>(It.IsAny<User>())).Returns(userServiceModel); - - UserServiceModel result = await this.UserService.GetUserByUsername(username); - - Assert.AreEqual(username, result.UserName, "GetUserByUsername does not return the correct user"); - } - - [Test] - public async Task GetUserByUsername_ThrowsException_IfUserDoesNotExist() - { - string username = "Gosho Trapov"; - const string EXCEPTION_MESSEGE = "User does not exist!"; - - this.UserRepositoryMock.Setup(p => p.GetByUsernameAsync(It.IsAny<string>())).Returns(Task.FromResult<User>(null)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.UserService.GetUserByUsername(username)); - - Assert.AreEqual(EXCEPTION_MESSEGE, ex.Message, "Incorecct exception message"); - } - #endregion - - #region UpdateUser - // [Test] - // [TestCase(true)] - // [TestCase(false)] - // public async Task UpdateUser_ReturnsIfUpdateIsSuccessfull_WhenUserExistsy(bool shouldPass) - // { - // string name = "Gosho Trapov"; - // Guid id = Guid.NewGuid(); - // User user = new User - // { - // UserName = name, - // Id = id, - // }; - // UpdateUserServiceModel updateUserServiceModel = new UpdateUserServiceModel - // { - // UserName = name, - // }; - // UserServiceModel userServiceModel = new UserServiceModel - // { - // UserName = name, - // }; - // Role role = new Role { }; - // - // this.UserRepositoryMock.Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - // this.UserRepositoryMock.Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(false)); - // this.UserRepositoryMock.Setup(p => p.DoesUserHaveThisUsername(It.IsAny<Guid>(), It.IsAny<string>())).Returns(true); - // this.UserRepositoryMock.Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<User>())).Returns(Task.FromResult(shouldPass)); - // this.UserRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(user)); - // this.MapperMock.Setup(p => p.Map<User>(It.IsAny<UpdateUserServiceModel>())).Returns(user); - // this.MapperMock.Setup(p => p.Map<UserServiceModel>(It.IsAny<User>())).Returns(userServiceModel); - // - // if (shouldPass) - // { - // UserServiceModel result = await this.UserService.UpdateUser(updateUserServiceModel); - // - // Assert.AreEqual(updateUserServiceModel.UserName, result.UserName); - // } - // else - // { - // const string EXCEPTION_MESSAGE = "Unable to edit user!"; - // - // Exception ex = Assert.ThrowsAsync<InvalidOperationException>(() => this.UserService.UpdateUser(updateUserServiceModel)); - // - // Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorecct exception message"); - // } - // } - - [Test] - public void UpdateUser_ThrowsException_WhenUserDoesNotExist() - { - const string EXCEPTION_MESSAGE = "User does not exist!"; - UpdateUserServiceModel updateUserServiceModel = new UpdateUserServiceModel - { - }; - - this.UserRepositoryMock.Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.UserService.UpdateUser(updateUserServiceModel)); - - Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorecct exception message"); - } - - [Test] - public void UpdateUser_ThrowsException_WhenUserNameAlreadyExists() - { - const string EXCEPTION_MESSAGE = "Username already exists!"; - UpdateUserServiceModel updateUserServiceModel = new UpdateUserServiceModel - { - }; - - this.UserRepositoryMock.Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.UserRepositoryMock.Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())).Returns(Task.FromResult(true)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.UserService.UpdateUser(updateUserServiceModel)); - - Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorecct exception message"); - } - #endregion - - #region DeleteUser - //TO DO: compleate once Viko has looked into the return type of UserService.DeleteUser - // [Test] - // [TestCase(true)] - // [TestCase(false)] - // public async Task DeleteUser_ShouldReturnIfDeletionIsSuccessfull_WhenUserExists(bool shouldPass) - // { - // Guid id = new Guid(); - // User user = new User(); - // - // this.UserRepositoryMock.Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - // this.UserRepositoryMock.Setup(p => p.GetByIdAsync(It.IsAny<Guid>())).Returns(Task.FromResult(user)); - // this.UserRepositoryMock.Setup(p => p.DeleteAsync(It.IsAny<User>())).Returns(Task.FromResult(shouldPass)); - // - // bool result = await this.UserService.DeleteUser(id); - // - // Assert.AreEqual(shouldPass, result); - // } - // - [Test] - public void DeleteUser_ThrowsException_WhenUserDoesNotExist() - { - string exceptionMessage = "User does not exist!"; - Guid id = new Guid(); - - this.UserRepositoryMock.Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this.UserService.DeleteUser(id)); - - Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); - } - #endregion - - #region HelperMethods - private string WriteJWTSecurityToken(Guid userId, string username, HashSet<Role> roles) - { - byte[] signingKey = Encoding.ASCII.GetBytes(this.JWTOptions.Secret); - HashSet<Claim> claims = new() - { - new Claim("ID", $"{userId}"), - new Claim("Username", username), - }; - - foreach (var role in roles) - { - claims.Add(new Claim(ClaimTypes.Role, role.Name)); - } - - SecurityTokenDescriptor tokenDescriptor = new() - { - Subject = new ClaimsIdentity(claims), - Expires = DateTime.Today.AddDays(7), - SigningCredentials = new SigningCredentials( - new SymmetricSecurityKey(signingKey), - SecurityAlgorithms.HmacSha512Signature) - }; - - JwtSecurityTokenHandler tokenHandler = new(); - SecurityToken token = tokenHandler.CreateToken(tokenDescriptor); - return tokenHandler.WriteToken(token); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Web.Tests/CommentController.Tests.cs b/src/DevHive.Tests/DevHive.Web.Tests/CommentController.Tests.cs deleted file mode 100644 index 3a03f1a..0000000 --- a/src/DevHive.Tests/DevHive.Web.Tests/CommentController.Tests.cs +++ /dev/null @@ -1,256 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Services.Interfaces; -using DevHive.Services.Models.Comment; -using DevHive.Web.Controllers; -using DevHive.Web.Models.Comment; -using Microsoft.AspNetCore.Mvc; -using Moq; -using NUnit.Framework; - -namespace DevHive.Web.Tests -{ - [TestFixture] - public class CommentControllerTests - { - const string MESSAGE = "Gosho Trapov"; - private Mock<ICommentService> CommentServiceMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private CommentController CommentController { get; set; } - - #region Setup - [SetUp] - public void SetUp() - { - this.CommentServiceMock = new Mock<ICommentService>(); - this.MapperMock = new Mock<IMapper>(); - this.CommentController = new CommentController(this.CommentServiceMock.Object, this.MapperMock.Object); - } - #endregion - - #region Add - [Test] - public void AddComment_ReturnsOkObjectResult_WhenCommentIsSuccessfullyCreated() - { - Guid id = Guid.NewGuid(); - CreateCommentWebModel createCommentWebModel = new CreateCommentWebModel - { - Message = MESSAGE - }; - CreateCommentServiceModel createCommentServiceModel = new CreateCommentServiceModel - { - Message = MESSAGE - }; - - this.MapperMock.Setup(p => p.Map<CreateCommentServiceModel>(It.IsAny<CreateCommentWebModel>())).Returns(createCommentServiceModel); - this.CommentServiceMock.Setup(p => p.AddComment(It.IsAny<CreateCommentServiceModel>())).Returns(Task.FromResult(id)); - this.CommentServiceMock.Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - - IActionResult result = this.CommentController.AddComment(Guid.NewGuid(), createCommentWebModel, null).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - var splitted = (result as OkObjectResult).Value - .ToString() - .Split('{', '}', '=', ' ') - .Where(x => !string.IsNullOrEmpty(x)) - .ToArray(); - - Guid resultId = Guid.Parse(splitted[1]); - - Assert.AreEqual(id, resultId); - } - - [Test] - public void AddComment_ReturnsBadRequestObjectResult_WhenCommentIsNotCreatedSuccessfully() - { - Guid id = Guid.NewGuid(); - CreateCommentWebModel createCommentWebModel = new CreateCommentWebModel - { - Message = MESSAGE - }; - CreateCommentServiceModel createCommentServiceModel = new CreateCommentServiceModel - { - Message = MESSAGE - }; - string errorMessage = $"Could not create comment!"; - - - this.MapperMock.Setup(p => p.Map<CreateCommentServiceModel>(It.IsAny<CreateCommentWebModel>())).Returns(createCommentServiceModel); - this.CommentServiceMock.Setup(p => p.AddComment(It.IsAny<CreateCommentServiceModel>())).Returns(Task.FromResult(Guid.Empty)); - this.CommentServiceMock.Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - - IActionResult result = this.CommentController.AddComment(Guid.NewGuid(), createCommentWebModel, null).Result; - - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequsetObjectResult = result as BadRequestObjectResult; - string resultMessage = badRequsetObjectResult.Value.ToString(); - - Assert.AreEqual(errorMessage, resultMessage); - } - - [Test] - public void AddComment_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() - { - Guid id = Guid.NewGuid(); - CreateCommentWebModel createCommentWebModel = new CreateCommentWebModel - { - Message = MESSAGE - }; - - this.CommentServiceMock.Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(false)); - - IActionResult result = this.CommentController.AddComment(Guid.NewGuid(), createCommentWebModel, null).Result; - - Assert.IsInstanceOf<UnauthorizedResult>(result); - } - #endregion - - #region Read - [Test] - public void GetById_ReturnsTheComment_WhenItExists() - { - Guid id = Guid.NewGuid(); - - ReadCommentServiceModel readCommentServiceModel = new ReadCommentServiceModel - { - Message = MESSAGE - }; - ReadCommentWebModel readCommentWebModel = new ReadCommentWebModel - { - Message = MESSAGE - }; - - this.CommentServiceMock.Setup(p => p.GetCommentById(It.IsAny<Guid>())).Returns(Task.FromResult(readCommentServiceModel)); - this.MapperMock.Setup(p => p.Map<ReadCommentWebModel>(It.IsAny<ReadCommentServiceModel>())).Returns(readCommentWebModel); - - IActionResult result = this.CommentController.GetCommentById(id).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - OkObjectResult okObjectResult = result as OkObjectResult; - ReadCommentWebModel resultModel = okObjectResult.Value as Models.Comment.ReadCommentWebModel; - - Assert.AreEqual(MESSAGE, resultModel.Message); - } - #endregion - - #region Update - [Test] - public void Update_ShouldReturnOkResult_WhenCommentIsUpdatedSuccessfully() - { - Guid id = Guid.NewGuid(); - UpdateCommentWebModel updateCommentWebModel = new UpdateCommentWebModel - { - NewMessage = MESSAGE - }; - UpdateCommentServiceModel updateCommentServiceModel = new UpdateCommentServiceModel - { - NewMessage = MESSAGE - }; - - this.CommentServiceMock.Setup(p => p.UpdateComment(It.IsAny<UpdateCommentServiceModel>())).Returns(Task.FromResult(id)); - this.CommentServiceMock.Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - this.MapperMock.Setup(p => p.Map<UpdateCommentServiceModel>(It.IsAny<UpdateCommentWebModel>())).Returns(updateCommentServiceModel); - - IActionResult result = this.CommentController.UpdateComment(Guid.Empty, updateCommentWebModel, null).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - OkObjectResult okObjectResult = result as OkObjectResult; - object resultModel = okObjectResult.Value; - string[] resultAsString = resultModel.ToString().Split(' ').ToArray(); - - Assert.AreEqual(id.ToString(), resultAsString[3]); - } - - [Test] - public void Update_ShouldReturnBadObjectResult_WhenCommentIsNotUpdatedSuccessfully() - { - string message = "Unable to update comment!"; - UpdateCommentWebModel updateCommentWebModel = new UpdateCommentWebModel - { - NewMessage = MESSAGE - }; - UpdateCommentServiceModel updateCommentServiceModel = new UpdateCommentServiceModel - { - NewMessage = MESSAGE - }; - - this.CommentServiceMock.Setup(p => p.UpdateComment(It.IsAny<UpdateCommentServiceModel>())).Returns(Task.FromResult(Guid.Empty)); - this.CommentServiceMock.Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - this.MapperMock.Setup(p => p.Map<UpdateCommentServiceModel>(It.IsAny<UpdateCommentWebModel>())).Returns(updateCommentServiceModel); - - IActionResult result = this.CommentController.UpdateComment(Guid.Empty, updateCommentWebModel, null).Result; - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; - string resultModel = badRequestObjectResult.Value.ToString(); - - Assert.AreEqual(message, resultModel); - } - - [Test] - public void Update_ShouldReturnUnauthorizedResult_WhenUserIsNotAuthorized() - { - UpdateCommentWebModel updateCommentWebModel = new UpdateCommentWebModel - { - NewMessage = MESSAGE - }; - - this.CommentServiceMock.Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(false)); - - IActionResult result = this.CommentController.UpdateComment(Guid.Empty, updateCommentWebModel, null).Result; - - Assert.IsInstanceOf<UnauthorizedResult>(result); - } - #endregion - - #region Delete - [Test] - public void Delete_ReturnsOkResult_WhenCommentIsDeletedSuccessfully() - { - Guid id = Guid.NewGuid(); - - this.CommentServiceMock.Setup(p => p.DeleteComment(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.CommentServiceMock.Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - - IActionResult result = this.CommentController.DeleteComment(id, null).Result; - - Assert.IsInstanceOf<OkResult>(result); - } - - [Test] - public void DeletComment_ReturnsBadRequestObjectResult_WhenCommentIsNotDeletedSuccessfully() - { - string message = "Could not delete Comment"; - Guid id = Guid.NewGuid(); - - this.CommentServiceMock.Setup(p => p.DeleteComment(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - this.CommentServiceMock.Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - - IActionResult result = this.CommentController.DeleteComment(id, null).Result; - - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; - string resultModel = badRequestObjectResult.Value.ToString(); - - Assert.AreEqual(message, resultModel); - } - - [Test] - public void DeletComment_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() - { - this.CommentServiceMock.Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(false)); - - IActionResult result = this.CommentController.DeleteComment(Guid.Empty, null).Result; - - Assert.IsInstanceOf<UnauthorizedResult>(result); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Web.Tests/FeedController.Tests.cs b/src/DevHive.Tests/DevHive.Web.Tests/FeedController.Tests.cs deleted file mode 100644 index 01f67e5..0000000 --- a/src/DevHive.Tests/DevHive.Web.Tests/FeedController.Tests.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Services.Interfaces; -using DevHive.Services.Models; -using DevHive.Web.Controllers; -using DevHive.Web.Models.Comment; -using DevHive.Web.Models.Feed; -using DevHive.Web.Models.Post; -using Microsoft.AspNetCore.Mvc; -using Moq; -using NUnit.Framework; - -namespace DevHive.Web.Tests -{ - [TestFixture] - public class FeedControllerTests - { - private Mock<IFeedService> FeedServiceMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private FeedController FeedController { get; set; } - - #region SetUp - [SetUp] - public void SetUp() - { - this.FeedServiceMock = new Mock<IFeedService>(); - this.MapperMock = new Mock<IMapper>(); - this.FeedController = new FeedController(this.FeedServiceMock.Object, this.MapperMock.Object); - } - #endregion - - #region GetPosts - [Test] - public async Task GetPosts_ReturnsOkObjectResultWithCorrectReadPageWebModel_WhenPostsExist() - { - GetPageWebModel getPageWebModel = new GetPageWebModel { }; - GetPageServiceModel getPageServiceModel = new GetPageServiceModel { }; - ReadPageServiceModel readPageServiceModel = new ReadPageServiceModel { }; - ReadPageWebModel readPageWebModel = new ReadPageWebModel - { - Posts = new List<ReadPostWebModel> - { - new ReadPostWebModel(), - new ReadPostWebModel(), - new ReadPostWebModel() - } - }; - - this.FeedServiceMock.Setup(p => p.GetPage(It.IsAny<GetPageServiceModel>())).Returns(Task.FromResult(readPageServiceModel)); - this.MapperMock.Setup(p => p.Map<GetPageServiceModel>(It.IsAny<GetPageWebModel>())).Returns(getPageServiceModel); - this.MapperMock.Setup(p => p.Map<ReadPageWebModel>(It.IsAny<ReadPageServiceModel>())).Returns(readPageWebModel); - - IActionResult result = await this.FeedController.GetPosts(Guid.Empty, getPageWebModel); - - Assert.IsInstanceOf<OkObjectResult>(result); - - OkObjectResult okObjectResult = result as OkObjectResult; - ReadPageWebModel resultModel = okObjectResult.Value as Models.Comment.ReadPageWebModel; - - Assert.AreEqual(3, resultModel.Posts.Count); - } - #endregion - - #region GetUserPosts - [Test] - public async Task GetUserPosts_GetsPostsOfUser_WhenTheyExist() - { - GetPageWebModel getPageWebModel = new GetPageWebModel { }; - GetPageServiceModel getPageServiceModel = new GetPageServiceModel { }; - ReadPageServiceModel readPageServiceModel = new ReadPageServiceModel { }; - ReadPageWebModel readPageWebModel = new ReadPageWebModel - { - Posts = new List<ReadPostWebModel> - { - new ReadPostWebModel(), - new ReadPostWebModel(), - new ReadPostWebModel() - } - }; - - this.FeedServiceMock.Setup(p => p.GetUserPage(It.IsAny<GetPageServiceModel>())).Returns(Task.FromResult(readPageServiceModel)); - this.MapperMock.Setup(p => p.Map<GetPageServiceModel>(It.IsAny<GetPageWebModel>())).Returns(getPageServiceModel); - this.MapperMock.Setup(p => p.Map<ReadPageWebModel>(It.IsAny<ReadPageServiceModel>())).Returns(readPageWebModel); - - IActionResult result = await this.FeedController.GetUserPosts(null, getPageWebModel); - - Assert.IsInstanceOf<OkObjectResult>(result); - - OkObjectResult okObjectResult = result as OkObjectResult; - ReadPageWebModel resultModel = okObjectResult.Value as Models.Comment.ReadPageWebModel; - - Assert.AreEqual(3, resultModel.Posts.Count); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Web.Tests/LanguageController.Tests.cs b/src/DevHive.Tests/DevHive.Web.Tests/LanguageController.Tests.cs deleted file mode 100644 index 7c8d64e..0000000 --- a/src/DevHive.Tests/DevHive.Web.Tests/LanguageController.Tests.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Services.Interfaces; -using DevHive.Services.Models.Language; -using DevHive.Web.Controllers; -using DevHive.Web.Models.Language; -using Microsoft.AspNetCore.Mvc; -using Moq; -using NUnit.Framework; - -namespace DevHive.Web.Tests -{ - [TestFixture] - public class LanguageControllerTests - { - const string NAME = "Gosho Trapov"; - private Mock<ILanguageService> LanguageServiceMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private LanguageController LanguageController { get; set; } - - [SetUp] - public void SetUp() - { - this.LanguageServiceMock = new Mock<ILanguageService>(); - this.MapperMock = new Mock<IMapper>(); - this.LanguageController = new LanguageController(this.LanguageServiceMock.Object, this.MapperMock.Object); - } - - #region Create - [Test] - public void CreateLanguage_ReturnsOkObjectResult_WhenLanguageIsSuccessfullyCreated() - { - CreateLanguageWebModel createLanguageWebModel = new CreateLanguageWebModel - { - Name = NAME - }; - CreateLanguageServiceModel createLanguageServiceModel = new CreateLanguageServiceModel - { - Name = NAME - }; - Guid id = Guid.NewGuid(); - - this.MapperMock.Setup(p => p.Map<CreateLanguageServiceModel>(It.IsAny<CreateLanguageWebModel>())).Returns(createLanguageServiceModel); - this.LanguageServiceMock.Setup(p => p.CreateLanguage(It.IsAny<CreateLanguageServiceModel>())).Returns(Task.FromResult(id)); - - IActionResult result = this.LanguageController.Create(createLanguageWebModel).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - var splitted = (result as OkObjectResult).Value - .ToString() - .Split('{', '}', '=', ' ') - .Where(x => !string.IsNullOrEmpty(x)) - .ToArray(); - - Guid resultId = Guid.Parse(splitted[1]); - - Assert.AreEqual(id, resultId); - } - - [Test] - public void CreateLanguage_ReturnsBadRequestObjectResult_WhenLanguageIsNotCreatedSuccessfully() - { - CreateLanguageWebModel createTechnologyWebModel = new CreateLanguageWebModel - { - Name = NAME - }; - CreateLanguageServiceModel createTechnologyServiceModel = new CreateLanguageServiceModel - { - Name = NAME - }; - Guid id = Guid.Empty; - string errorMessage = $"Could not create language {NAME}"; - - this.MapperMock.Setup(p => p.Map<CreateLanguageServiceModel>(It.IsAny<CreateLanguageWebModel>())).Returns(createTechnologyServiceModel); - this.LanguageServiceMock.Setup(p => p.CreateLanguage(It.IsAny<CreateLanguageServiceModel>())).Returns(Task.FromResult(id)); - - IActionResult result = this.LanguageController.Create(createTechnologyWebModel).Result; - - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequsetObjectResult = result as BadRequestObjectResult; - string resultMessage = badRequsetObjectResult.Value.ToString(); - - Assert.AreEqual(errorMessage, resultMessage); - } - #endregion - - #region Read - [Test] - public void GetById_ReturnsTheLanguage_WhenItExists() - { - Guid id = Guid.NewGuid(); - - ReadLanguageServiceModel readLanguageServiceModel = new ReadLanguageServiceModel - { - Name = NAME - }; - ReadLanguageWebModel readLanguageWebModel = new ReadLanguageWebModel - { - Name = NAME - }; - - this.LanguageServiceMock.Setup(p => p.GetLanguageById(It.IsAny<Guid>())).Returns(Task.FromResult(readLanguageServiceModel)); - this.MapperMock.Setup(p => p.Map<ReadLanguageWebModel>(It.IsAny<ReadLanguageServiceModel>())).Returns(readLanguageWebModel); - - IActionResult result = this.LanguageController.GetById(id).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - OkObjectResult okObjectResult = result as OkObjectResult; - ReadLanguageWebModel resultModel = okObjectResult.Value as Models.Language.ReadLanguageWebModel; - - Assert.AreEqual(NAME, resultModel.Name); - } - #endregion - - #region Update - [Test] - public void Update_ShouldReturnOkResult_WhenLanguageIsUpdatedSuccessfully() - { - Guid id = Guid.NewGuid(); - UpdateLanguageWebModel updateLanguageWebModel = new UpdateLanguageWebModel - { - Name = NAME - }; - UpdateLanguageServiceModel updateLanguageServiceModel = new UpdateLanguageServiceModel - { - Name = NAME - }; - - this.LanguageServiceMock.Setup(p => p.UpdateLanguage(It.IsAny<UpdateLanguageServiceModel>())).Returns(Task.FromResult(true)); - this.MapperMock.Setup(p => p.Map<UpdateLanguageServiceModel>(It.IsAny<UpdateLanguageWebModel>())).Returns(updateLanguageServiceModel); - - IActionResult result = this.LanguageController.Update(id, updateLanguageWebModel).Result; - - Assert.IsInstanceOf<OkResult>(result); - } - - [Test] - public void Update_ShouldReturnBadObjectResult_WhenLanguageIsNotUpdatedSuccessfully() - { - Guid id = Guid.NewGuid(); - string message = "Could not update Language"; - UpdateLanguageWebModel updateLanguageWebModel = new UpdateLanguageWebModel - { - Name = NAME - }; - UpdateLanguageServiceModel updateLanguageServiceModel = new UpdateLanguageServiceModel - { - Name = NAME - }; - - this.LanguageServiceMock.Setup(p => p.UpdateLanguage(It.IsAny<UpdateLanguageServiceModel>())).Returns(Task.FromResult(false)); - this.MapperMock.Setup(p => p.Map<UpdateLanguageServiceModel>(It.IsAny<UpdateLanguageWebModel>())).Returns(updateLanguageServiceModel); - - IActionResult result = this.LanguageController.Update(id, updateLanguageWebModel).Result; - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; - string resultModel = badRequestObjectResult.Value.ToString(); - - Assert.AreEqual(message, resultModel); - } - #endregion - - #region Delete - [Test] - public void Delete_ReturnsOkResult_WhenLanguageIsDeletedSuccessfully() - { - Guid id = Guid.NewGuid(); - - this.LanguageServiceMock.Setup(p => p.DeleteLanguage(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - - IActionResult result = this.LanguageController.Delete(id).Result; - - Assert.IsInstanceOf<OkResult>(result); - } - - [Test] - public void Delet_ReturnsBadRequestObjectResult_WhenLanguageIsNotDeletedSuccessfully() - { - string message = "Could not delete Language"; - Guid id = Guid.NewGuid(); - - this.LanguageServiceMock.Setup(p => p.DeleteLanguage(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - IActionResult result = this.LanguageController.Delete(id).Result; - - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; - string resultModel = badRequestObjectResult.Value.ToString(); - - Assert.AreEqual(message, resultModel); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Web.Tests/PostController.Tests.cs b/src/DevHive.Tests/DevHive.Web.Tests/PostController.Tests.cs deleted file mode 100644 index 3a4e45e..0000000 --- a/src/DevHive.Tests/DevHive.Web.Tests/PostController.Tests.cs +++ /dev/null @@ -1,248 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Services.Interfaces; -using DevHive.Services.Models.Post; -using DevHive.Web.Controllers; -using DevHive.Web.Models.Post; -using Microsoft.AspNetCore.Mvc; -using Moq; -using NUnit.Framework; - -namespace DevHive.Web.Tests -{ - [TestFixture] - public class PostControllerTests - { - const string MESSAGE = "Gosho Trapov"; - private Mock<IPostService> PostServiceMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private PostController PostController { get; set; } - - [SetUp] - public void SetUp() - { - this.PostServiceMock = new Mock<IPostService>(); - this.MapperMock = new Mock<IMapper>(); - this.PostController = new PostController(this.PostServiceMock.Object, this.MapperMock.Object); - } - - #region Create - [Test] - public void CreatePost_ReturnsOkObjectResult_WhenPostIsSuccessfullyCreated() - { - CreatePostWebModel createPostWebModel = new CreatePostWebModel - { - Message = MESSAGE - }; - CreatePostServiceModel createPostServiceModel = new CreatePostServiceModel - { - Message = MESSAGE - }; - Guid id = Guid.NewGuid(); - - this.MapperMock.Setup(p => p.Map<CreatePostServiceModel>(It.IsAny<CreatePostWebModel>())).Returns(createPostServiceModel); - this.PostServiceMock.Setup(p => p.CreatePost(It.IsAny<CreatePostServiceModel>())).Returns(Task.FromResult(id)); - this.PostServiceMock.Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - - IActionResult result = this.PostController.Create(Guid.Empty, createPostWebModel, null).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - var splitted = (result as OkObjectResult).Value - .ToString() - .Split('{', '}', '=', ' ') - .Where(x => !string.IsNullOrEmpty(x)) - .ToArray(); - - Guid resultId = Guid.Parse(splitted[1]); - - Assert.AreEqual(id, resultId); - } - - [Test] - public void CreatePost_ReturnsBadRequestObjectResult_WhenPostIsNotCreatedSuccessfully() - { - CreatePostWebModel createTechnologyWebModel = new CreatePostWebModel - { - Message = MESSAGE - }; - CreatePostServiceModel createTechnologyServiceModel = new CreatePostServiceModel - { - Message = MESSAGE - }; - Guid id = Guid.Empty; - string errorMessage = $"Could not create post!"; - - this.MapperMock.Setup(p => p.Map<CreatePostServiceModel>(It.IsAny<CreatePostWebModel>())).Returns(createTechnologyServiceModel); - this.PostServiceMock.Setup(p => p.CreatePost(It.IsAny<CreatePostServiceModel>())).Returns(Task.FromResult(id)); - this.PostServiceMock.Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - - IActionResult result = this.PostController.Create(Guid.Empty, createTechnologyWebModel, null).Result; - - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequsetObjectResult = result as BadRequestObjectResult; - string resultMessage = badRequsetObjectResult.Value.ToString(); - - Assert.AreEqual(errorMessage, resultMessage); - } - - [Test] - public void CreatePost_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() - { - Guid id = Guid.NewGuid(); - CreatePostWebModel createPostWebModel = new CreatePostWebModel - { - Message = MESSAGE - }; - - this.PostServiceMock.Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(false)); - - IActionResult result = this.PostController.Create(Guid.NewGuid(), createPostWebModel, null).Result; - - Assert.IsInstanceOf<UnauthorizedResult>(result); - } - #endregion - - #region Read - [Test] - public void GetById_ReturnsThePost_WhenItExists() - { - Guid id = Guid.NewGuid(); - - ReadPostServiceModel readPostServiceModel = new ReadPostServiceModel - { - Message = MESSAGE - }; - ReadPostWebModel readPostWebModel = new ReadPostWebModel - { - Message = MESSAGE - }; - - this.PostServiceMock.Setup(p => p.GetPostById(It.IsAny<Guid>())).Returns(Task.FromResult(readPostServiceModel)); - this.MapperMock.Setup(p => p.Map<ReadPostWebModel>(It.IsAny<ReadPostServiceModel>())).Returns(readPostWebModel); - - IActionResult result = this.PostController.GetById(id).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - OkObjectResult okObjectResult = result as OkObjectResult; - ReadPostWebModel resultModel = okObjectResult.Value as Models.Post.ReadPostWebModel; - - Assert.AreEqual(MESSAGE, resultModel.Message); - } - #endregion - - #region Update - [Test] - public void Update_ShouldReturnOkResult_WhenPostIsUpdatedSuccessfully() - { - Guid id = Guid.NewGuid(); - UpdatePostWebModel updatePostWebModel = new UpdatePostWebModel - { - NewMessage = MESSAGE - }; - UpdatePostServiceModel updatePostServiceModel = new UpdatePostServiceModel - { - NewMessage = MESSAGE - }; - - this.PostServiceMock.Setup(p => p.UpdatePost(It.IsAny<UpdatePostServiceModel>())).Returns(Task.FromResult(id)); - this.MapperMock.Setup(p => p.Map<UpdatePostServiceModel>(It.IsAny<UpdatePostWebModel>())).Returns(updatePostServiceModel); - this.PostServiceMock.Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - - IActionResult result = this.PostController.Update(id, updatePostWebModel, null).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - } - - [Test] - public void Update_ShouldReturnBadObjectResult_WhenPostIsNotUpdatedSuccessfully() - { - Guid id = Guid.NewGuid(); - string message = "Could not update post!"; - UpdatePostWebModel updatePostWebModel = new UpdatePostWebModel - { - NewMessage = MESSAGE - }; - UpdatePostServiceModel updatePostServiceModel = new UpdatePostServiceModel - { - NewMessage = MESSAGE - }; - - this.PostServiceMock.Setup(p => p.UpdatePost(It.IsAny<UpdatePostServiceModel>())).Returns(Task.FromResult(Guid.Empty)); - this.MapperMock.Setup(p => p.Map<UpdatePostServiceModel>(It.IsAny<UpdatePostWebModel>())).Returns(updatePostServiceModel); - this.PostServiceMock.Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - - IActionResult result = this.PostController.Update(id, updatePostWebModel, null).Result; - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; - string resultModel = badRequestObjectResult.Value.ToString(); - - Assert.AreEqual(message, resultModel); - } - - [Test] - public void Update_ShouldReturnUnauthorizedResult_WhenUserIsNotAuthorized() - { - UpdatePostWebModel updatePostWebModel = new UpdatePostWebModel - { - NewMessage = MESSAGE - }; - - this.PostServiceMock.Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(false)); - - IActionResult result = this.PostController.Update(Guid.Empty, updatePostWebModel, null).Result; - - Assert.IsInstanceOf<UnauthorizedResult>(result); - } - #endregion - - #region Delete - [Test] - public void Delete_ReturnsOkResult_WhenPostIsDeletedSuccessfully() - { - Guid id = Guid.NewGuid(); - - this.PostServiceMock.Setup(p => p.DeletePost(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - this.PostServiceMock.Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - - IActionResult result = this.PostController.Delete(id, null).Result; - - Assert.IsInstanceOf<OkResult>(result); - } - - [Test] - public void Delete_ReturnsBadRequestObjectResult_WhenPostIsNotDeletedSuccessfully() - { - string message = "Could not delete Post"; - Guid id = Guid.NewGuid(); - - this.PostServiceMock.Setup(p => p.DeletePost(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - this.PostServiceMock.Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - - IActionResult result = this.PostController.Delete(id, null).Result; - - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; - string resultModel = badRequestObjectResult.Value.ToString(); - - Assert.AreEqual(message, resultModel); - } - - [Test] - public void DeletePost_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() - { - this.PostServiceMock.Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(false)); - - IActionResult result = this.PostController.Delete(Guid.Empty, null).Result; - - Assert.IsInstanceOf<UnauthorizedResult>(result); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Web.Tests/RoleController.Tests.cs b/src/DevHive.Tests/DevHive.Web.Tests/RoleController.Tests.cs deleted file mode 100644 index 067b4e4..0000000 --- a/src/DevHive.Tests/DevHive.Web.Tests/RoleController.Tests.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Services.Interfaces; -using DevHive.Services.Models.Identity.Role; -using DevHive.Web.Controllers; -using DevHive.Web.Models.Identity.Role; -using Microsoft.AspNetCore.Mvc; -using Moq; -using NUnit.Framework; - -namespace DevHive.Web.Tests -{ - [TestFixture] - public class RoleControllerTests - { - const string NAME = "Gosho Trapov"; - private Mock<IRoleService> RoleServiceMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private RoleController RoleController { get; set; } - - [SetUp] - public void SetUp() - { - this.RoleServiceMock = new Mock<IRoleService>(); - this.MapperMock = new Mock<IMapper>(); - this.RoleController = new RoleController(this.RoleServiceMock.Object, this.MapperMock.Object); - } - - #region Create - [Test] - public void CreateRole_ReturnsOkObjectResult_WhenRoleIsSuccessfullyCreated() - { - CreateRoleWebModel createRoleWebModel = new CreateRoleWebModel - { - Name = NAME - }; - CreateRoleServiceModel createRoleServiceModel = new CreateRoleServiceModel - { - Name = NAME - }; - Guid id = Guid.NewGuid(); - - this.MapperMock.Setup(p => p.Map<CreateRoleServiceModel>(It.IsAny<CreateRoleWebModel>())).Returns(createRoleServiceModel); - this.RoleServiceMock.Setup(p => p.CreateRole(It.IsAny<CreateRoleServiceModel>())).Returns(Task.FromResult(id)); - - IActionResult result = this.RoleController.Create(createRoleWebModel).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - var splitted = (result as OkObjectResult).Value - .ToString() - .Split('{', '}', '=', ' ') - .Where(x => !string.IsNullOrEmpty(x)) - .ToArray(); - - Guid resultId = Guid.Parse(splitted[1]); - - Assert.AreEqual(id, resultId); - } - - [Test] - public void CreateRole_ReturnsBadRequestObjectResult_WhenRoleIsNotCreatedSuccessfully() - { - CreateRoleWebModel createTechnologyWebModel = new CreateRoleWebModel - { - Name = NAME - }; - CreateRoleServiceModel createTechnologyServiceModel = new CreateRoleServiceModel - { - Name = NAME - }; - Guid id = Guid.Empty; - string errorMessage = $"Could not create role {NAME}"; - - this.MapperMock.Setup(p => p.Map<CreateRoleServiceModel>(It.IsAny<CreateRoleWebModel>())).Returns(createTechnologyServiceModel); - this.RoleServiceMock.Setup(p => p.CreateRole(It.IsAny<CreateRoleServiceModel>())).Returns(Task.FromResult(id)); - - IActionResult result = this.RoleController.Create(createTechnologyWebModel).Result; - - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequsetObjectResult = result as BadRequestObjectResult; - string resultMessage = badRequsetObjectResult.Value.ToString(); - - Assert.AreEqual(errorMessage, resultMessage); - } - #endregion - - #region Read - [Test] - public void GetById_ReturnsTheRole_WhenItExists() - { - Guid id = Guid.NewGuid(); - - RoleServiceModel roleServiceModel = new RoleServiceModel - { - Name = NAME - }; - RoleWebModel roleWebModel = new RoleWebModel - { - Name = NAME - }; - - this.RoleServiceMock.Setup(p => p.GetRoleById(It.IsAny<Guid>())).Returns(Task.FromResult(roleServiceModel)); - this.MapperMock.Setup(p => p.Map<RoleWebModel>(It.IsAny<RoleServiceModel>())).Returns(roleWebModel); - - IActionResult result = this.RoleController.GetById(id).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - OkObjectResult okObjectResult = result as OkObjectResult; - RoleWebModel resultModel = okObjectResult.Value as Models.Identity.Role.RoleWebModel; - - Assert.AreEqual(NAME, resultModel.Name); - } - #endregion - - #region Update - [Test] - public void Update_ShouldReturnOkResult_WhenRoleIsUpdatedSuccessfully() - { - Guid id = Guid.NewGuid(); - UpdateRoleWebModel updateRoleWebModel = new UpdateRoleWebModel - { - Name = NAME - }; - UpdateRoleServiceModel updateRoleServiceModel = new UpdateRoleServiceModel - { - Name = NAME - }; - - this.RoleServiceMock.Setup(p => p.UpdateRole(It.IsAny<UpdateRoleServiceModel>())).Returns(Task.FromResult(true)); - this.MapperMock.Setup(p => p.Map<UpdateRoleServiceModel>(It.IsAny<UpdateRoleWebModel>())).Returns(updateRoleServiceModel); - - IActionResult result = this.RoleController.Update(id, updateRoleWebModel).Result; - - Assert.IsInstanceOf<OkResult>(result); - } - - [Test] - public void Update_ShouldReturnBadObjectResult_WhenRoleIsNotUpdatedSuccessfully() - { - Guid id = Guid.NewGuid(); - string message = "Could not update role!"; - UpdateRoleWebModel updateRoleWebModel = new UpdateRoleWebModel - { - Name = NAME - }; - UpdateRoleServiceModel updateRoleServiceModel = new UpdateRoleServiceModel - { - Name = NAME - }; - - this.RoleServiceMock.Setup(p => p.UpdateRole(It.IsAny<UpdateRoleServiceModel>())).Returns(Task.FromResult(false)); - this.MapperMock.Setup(p => p.Map<UpdateRoleServiceModel>(It.IsAny<UpdateRoleWebModel>())).Returns(updateRoleServiceModel); - - IActionResult result = this.RoleController.Update(id, updateRoleWebModel).Result; - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; - string resultModel = badRequestObjectResult.Value.ToString(); - - Assert.AreEqual(message, resultModel); - } - #endregion - - #region Delete - [Test] - public void Delete_ReturnsOkResult_WhenRoleIsDeletedSuccessfully() - { - Guid id = Guid.NewGuid(); - - this.RoleServiceMock.Setup(p => p.DeleteRole(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - - IActionResult result = this.RoleController.Delete(id).Result; - - Assert.IsInstanceOf<OkResult>(result); - } - - [Test] - public void Delet_ReturnsBadRequestObjectResult_WhenRoleIsNotDeletedSuccessfully() - { - string message = "Could not delete role!"; - Guid id = Guid.NewGuid(); - - this.RoleServiceMock.Setup(p => p.DeleteRole(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - IActionResult result = this.RoleController.Delete(id).Result; - - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; - string resultModel = badRequestObjectResult.Value.ToString(); - - Assert.AreEqual(message, resultModel); - } - #endregion - } -} diff --git a/src/DevHive.Tests/DevHive.Web.Tests/UserController.Tests.cs b/src/DevHive.Tests/DevHive.Web.Tests/UserController.Tests.cs deleted file mode 100644 index c1431c8..0000000 --- a/src/DevHive.Tests/DevHive.Web.Tests/UserController.Tests.cs +++ /dev/null @@ -1,257 +0,0 @@ -using System; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Common.Models.Identity; -using DevHive.Services.Interfaces; -using DevHive.Services.Models.Identity.User; -using DevHive.Web.Controllers; -using DevHive.Web.Models.Identity.User; -using Microsoft.AspNetCore.Mvc; -using Moq; -using NUnit.Framework; - -namespace DevHive.Web.Tests -{ - [TestFixture] - public class UserControllerTests - { - const string USERNAME = "Gosho Trapov"; - private Mock<IUserService> UserServiceMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private UserController UserController { get; set; } - - [SetUp] - public void SetUp() - { - this.UserServiceMock = new Mock<IUserService>(); - this.MapperMock = new Mock<IMapper>(); - this.UserController = new UserController(this.UserServiceMock.Object, this.MapperMock.Object); - } - - #region Create - [Test] - public void LoginUser_ReturnsOkObjectResult_WhenUserIsSuccessfullyLoggedIn() - { - Guid id = Guid.NewGuid(); - LoginWebModel loginWebModel = new LoginWebModel - { - UserName = USERNAME - }; - LoginServiceModel loginServiceModel = new LoginServiceModel - { - UserName = USERNAME - }; - string token = "goshotrapov"; - TokenModel tokenModel = new TokenModel(token); - TokenWebModel tokenWebModel = new TokenWebModel(token); - - this.MapperMock.Setup(p => p.Map<LoginServiceModel>(It.IsAny<LoginWebModel>())).Returns(loginServiceModel); - this.MapperMock.Setup(p => p.Map<TokenWebModel>(It.IsAny<TokenModel>())).Returns(tokenWebModel); - this.UserServiceMock.Setup(p => p.LoginUser(It.IsAny<LoginServiceModel>())).Returns(Task.FromResult(tokenModel)); - - IActionResult result = this.UserController.Login(loginWebModel).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - var resultToken = ((result as OkObjectResult).Value as TokenWebModel).Token; - - Assert.AreEqual(token, resultToken); - } - - [Test] - public void RegisterUser_ReturnsOkObjectResult_WhenUserIsSuccessfullyRegistered() - { - Guid id = Guid.NewGuid(); - RegisterWebModel registerWebModel = new RegisterWebModel - { - UserName = USERNAME - }; - RegisterServiceModel registerServiceModel = new RegisterServiceModel - { - UserName = USERNAME - }; - string token = "goshotrapov"; - TokenModel tokenModel = new TokenModel(token); - TokenWebModel tokenWebModel = new TokenWebModel(token); - - this.MapperMock.Setup(p => p.Map<RegisterServiceModel>(It.IsAny<RegisterWebModel>())).Returns(registerServiceModel); - this.MapperMock.Setup(p => p.Map<TokenWebModel>(It.IsAny<TokenModel>())).Returns(tokenWebModel); - this.UserServiceMock.Setup(p => p.RegisterUser(It.IsAny<RegisterServiceModel>())).Returns(Task.FromResult(tokenModel)); - - IActionResult result = this.UserController.Register(registerWebModel).Result; - - Assert.IsInstanceOf<CreatedResult>(result); - - CreatedResult createdResult = result as CreatedResult; - TokenWebModel resultModel = (createdResult.Value as TokenWebModel); - - Assert.AreEqual(token, resultModel.Token); - } - #endregion - - #region Read - [Test] - public void GetById_ReturnsTheUser_WhenItExists() - { - Guid id = Guid.NewGuid(); - - UserServiceModel userServiceModel = new UserServiceModel - { - UserName = USERNAME - }; - UserWebModel userWebModel = new UserWebModel - { - UserName = USERNAME - }; - - this.UserServiceMock.Setup(p => p.GetUserById(It.IsAny<Guid>())).Returns(Task.FromResult(userServiceModel)); - this.UserServiceMock.Setup(p => p.ValidJWT(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - this.MapperMock.Setup(p => p.Map<UserWebModel>(It.IsAny<UserServiceModel>())).Returns(userWebModel); - - IActionResult result = this.UserController.GetById(id, null).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - OkObjectResult okObjectResult = result as OkObjectResult; - UserWebModel resultModel = okObjectResult.Value as Models.Identity.User.UserWebModel; - - Assert.AreEqual(USERNAME, resultModel.UserName); - } - - [Test] - public void GetById_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() - { - Guid id = Guid.NewGuid(); - UserWebModel userWebModel = new UserWebModel - { - UserName = USERNAME - }; - - this.UserServiceMock.Setup(p => p.ValidJWT(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(false)); - - IActionResult result = this.UserController.GetById(Guid.NewGuid(), null).Result; - - Assert.IsInstanceOf<UnauthorizedResult>(result); - } - - [Test] - public void GetUser_ReturnsTheUser_WhenItExists() - { - Guid id = Guid.NewGuid(); - UserWebModel userWebModel = new UserWebModel - { - UserName = USERNAME - }; - UserServiceModel userServiceModel = new UserServiceModel - { - UserName = USERNAME - }; - - this.UserServiceMock.Setup(p => p.GetUserByUsername(It.IsAny<string>())).Returns(Task.FromResult(userServiceModel)); - this.MapperMock.Setup(p => p.Map<UserWebModel>(It.IsAny<UserServiceModel>())).Returns(userWebModel); - - IActionResult result = this.UserController.GetUser(null).Result; - - Assert.IsInstanceOf<OkObjectResult>(result); - - OkObjectResult okObjectResult = result as OkObjectResult; - UserWebModel resultModel = okObjectResult.Value as Models.Identity.User.UserWebModel; - - Assert.AreEqual(USERNAME, resultModel.UserName); - } - #endregion - - #region Update - [Test] - public void Update_ShouldReturnOkResult_WhenUserIsUpdatedSuccessfully() - { - Guid id = Guid.NewGuid(); - UpdateUserWebModel updateUserWebModel = new UpdateUserWebModel - { - UserName = USERNAME - }; - UpdateUserServiceModel updateUserServiceModel = new UpdateUserServiceModel - { - UserName = USERNAME - }; - UserServiceModel userServiceModel = new UserServiceModel - { - UserName = USERNAME - }; - - this.UserServiceMock.Setup(p => p.UpdateUser(It.IsAny<UpdateUserServiceModel>())).Returns(Task.FromResult(userServiceModel)); - this.UserServiceMock.Setup(p => p.ValidJWT(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - this.MapperMock.Setup(p => p.Map<UpdateUserServiceModel>(It.IsAny<UpdateUserWebModel>())).Returns(updateUserServiceModel); - - IActionResult result = this.UserController.Update(id, updateUserWebModel, null).Result; - - Assert.IsInstanceOf<AcceptedResult>(result); - } - - [Test] - public void UpdateProfilePicture_ShouldReturnOkObjectResult_WhenProfilePictureIsUpdatedSuccessfully() - { - string profilePictureURL = "goshotrapov"; - UpdateProfilePictureWebModel updateProfilePictureWebModel = new UpdateProfilePictureWebModel(); - UpdateProfilePictureServiceModel updateProfilePictureServiceModel = new UpdateProfilePictureServiceModel(); - ProfilePictureServiceModel profilePictureServiceModel = new ProfilePictureServiceModel - { - ProfilePictureURL = profilePictureURL - }; - ProfilePictureWebModel profilePictureWebModel = new ProfilePictureWebModel - { - ProfilePictureURL = profilePictureURL - }; - - this.UserServiceMock.Setup(p => p.ValidJWT(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - this.MapperMock.Setup(p => p.Map<UpdateProfilePictureServiceModel>(It.IsAny<UpdateProfilePictureWebModel>())).Returns(updateProfilePictureServiceModel); - this.UserServiceMock.Setup(p => p.UpdateProfilePicture(It.IsAny<UpdateProfilePictureServiceModel>())).Returns(Task.FromResult(profilePictureServiceModel)); - this.MapperMock.Setup(p => p.Map<ProfilePictureWebModel>(It.IsAny<ProfilePictureServiceModel>())).Returns(profilePictureWebModel); - - - IActionResult result = this.UserController.UpdateProfilePicture(Guid.Empty, updateProfilePictureWebModel, null).Result; - - Assert.IsInstanceOf<AcceptedResult>(result); - - AcceptedResult acceptedResult = result as AcceptedResult; - ProfilePictureWebModel resultModel = acceptedResult.Value as Models.Identity.User.ProfilePictureWebModel; - - Assert.AreEqual(profilePictureURL, resultModel.ProfilePictureURL); - } - #endregion - - #region Delete - [Test] - public void Delete_ReturnsOkResult_WhenUserIsDeletedSuccessfully() - { - Guid id = Guid.NewGuid(); - - this.UserServiceMock.Setup(p => p.ValidJWT(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - this.UserServiceMock.Setup(p => p.DeleteUser(It.IsAny<Guid>())).Returns(Task.FromResult(true)); - - IActionResult result = this.UserController.Delete(id, null).Result; - - Assert.IsInstanceOf<OkResult>(result); - } - - [Test] - public void Delete_ReturnsBadRequestObjectResult_WhenUserIsNotDeletedSuccessfully() - { - string message = "Could not delete User"; - Guid id = Guid.NewGuid(); - - this.UserServiceMock.Setup(p => p.ValidJWT(It.IsAny<Guid>(), It.IsAny<string>())).Returns(Task.FromResult(true)); - this.UserServiceMock.Setup(p => p.DeleteUser(It.IsAny<Guid>())).Returns(Task.FromResult(false)); - - IActionResult result = this.UserController.Delete(id, null).Result; - - Assert.IsInstanceOf<BadRequestObjectResult>(result); - - BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; - string resultModel = badRequestObjectResult.Value.ToString(); - - Assert.AreEqual(message, resultModel); - } - #endregion - } -} diff --git a/src/DevHive.Web/Configurations/Extensions/ConfigureExceptionHandlerMiddleware.cs b/src/DevHive.Web/Configurations/Extensions/ConfigureExceptionHandlerMiddleware.cs deleted file mode 100644 index 286727f..0000000 --- a/src/DevHive.Web/Configurations/Extensions/ConfigureExceptionHandlerMiddleware.cs +++ /dev/null @@ -1,16 +0,0 @@ -using DevHive.Web.Middleware; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -namespace DevHive.Web.Configurations.Extensions -{ - public static class ConfigureExceptionHandlerMiddleware - { - public static void ExceptionHandlerMiddlewareConfiguration(this IServiceCollection services) { } - - public static void UseExceptionHandlerMiddlewareConfiguration(this IApplicationBuilder app) - { - app.UseMiddleware<ExceptionMiddleware>(); - } - } -} diff --git a/src/DevHive.Web/Configurations/Extensions/ConfigureSwagger.cs b/src/DevHive.Web/Configurations/Extensions/ConfigureSwagger.cs deleted file mode 100644 index a0641ab..0000000 --- a/src/DevHive.Web/Configurations/Extensions/ConfigureSwagger.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; - -namespace DevHive.Web.Configurations.Extensions -{ - public static class SwaggerExtensions - { - public static void SwaggerConfiguration(this IServiceCollection services) - { - services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" }); - }); - } - - public static void UseSwaggerConfiguration(this IApplicationBuilder app) - { - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1")); - } - } -}
\ No newline at end of file diff --git a/src/DevHive.Web/Configurations/Mapping/RatingMappings.cs b/src/DevHive.Web/Configurations/Mapping/RatingMappings.cs deleted file mode 100644 index 4e071de..0000000 --- a/src/DevHive.Web/Configurations/Mapping/RatingMappings.cs +++ /dev/null @@ -1,16 +0,0 @@ -using AutoMapper; -using DevHive.Services.Models.Post.Rating; -using DevHive.Web.Models.Post.Rating; - -namespace DevHive.Web.Configurations.Mapping -{ - public class RatingMappings : Profile - { - public RatingMappings() - { - CreateMap<RatePostWebModel, RatePostServiceModel>(); - - CreateMap<ReadPostRatingServiceModel, ReadPostRatingWebModel>(); - } - } -} diff --git a/src/DevHive.Web/Controllers/RateController.cs b/src/DevHive.Web/Controllers/RateController.cs deleted file mode 100644 index 68b859b..0000000 --- a/src/DevHive.Web/Controllers/RateController.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Threading.Tasks; -using AutoMapper; -using DevHive.Services.Interfaces; -using DevHive.Services.Models.Post.Rating; -using DevHive.Web.Models.Post.Rating; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace DevHive.Web.Controllers -{ - [ApiController] - [Route("api/[controller]")] - public class RateController - { - private readonly IRateService _rateService; - private readonly IUserService _userService; - private readonly IMapper _mapper; - - public RateController(IRateService rateService, IUserService userService, IMapper mapper) - { - this._rateService = rateService; - this._userService = userService; - this._mapper = mapper; - } - - [HttpPost] - [Authorize(Roles = "Admin,User")] - public async Task<IActionResult> RatePost(Guid userId, [FromBody] RatePostWebModel ratePostWebModel, [FromHeader] string authorization) - { - RatePostServiceModel ratePostServiceModel = this._mapper.Map<RatePostServiceModel>(ratePostWebModel); - ratePostServiceModel.UserId = userId; - - ReadPostRatingServiceModel readPostRatingServiceModel = await this._rateService.RatePost(ratePostServiceModel); - ReadPostRatingWebModel readPostRatingWebModel = this._mapper.Map<ReadPostRatingWebModel>(readPostRatingServiceModel); - - return new OkObjectResult(readPostRatingWebModel); - } - } -} diff --git a/src/DevHive.Web/DevHive.Web.csproj b/src/DevHive.Web/DevHive.Web.csproj deleted file mode 100644 index 0aa2831..0000000 --- a/src/DevHive.Web/DevHive.Web.csproj +++ /dev/null @@ -1,27 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk.Web"> - <PropertyGroup> - <TargetFramework>net5.0</TargetFramework> - </PropertyGroup> - <PropertyGroup> - <EnableNETAnalyzers>true</EnableNETAnalyzers> - <AnalysisLevel>latest</AnalysisLevel> - </PropertyGroup> - <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.1" NoWarn="NU1605"/> - <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.1" NoWarn="NU1605"/> - <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.1"> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - <PrivateAssets>all</PrivateAssets> - </PackageReference> - <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.1"/> - <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3"/> - <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0"/> - <PackageReference Include="AutoMapper" Version="10.1.1"/> - <PackageReference Include="Newtonsoft.Json" Version="12.0.3"/> - <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.1"/> - </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\DevHive.Services\DevHive.Services.csproj"/> - <ProjectReference Include="..\DevHive.Common\DevHive.Common.csproj"/> - </ItemGroup> -</Project> diff --git a/src/DevHive.Web/Models/Identity/User/ProfilePictureWebModel.cs b/src/DevHive.Web/Models/Identity/User/ProfilePictureWebModel.cs deleted file mode 100644 index 9fb1516..0000000 --- a/src/DevHive.Web/Models/Identity/User/ProfilePictureWebModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace DevHive.Web.Models.Identity.User -{ - public class ProfilePictureWebModel - { - public string ProfilePictureURL { get; set; } - } -} diff --git a/src/DevHive.Web/Models/Identity/User/UpdateProfilePictureWebModel.cs b/src/DevHive.Web/Models/Identity/User/UpdateProfilePictureWebModel.cs deleted file mode 100644 index 6efe968..0000000 --- a/src/DevHive.Web/Models/Identity/User/UpdateProfilePictureWebModel.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.AspNetCore.Http; - -namespace DevHive.Web.Models.Identity.User -{ - public class UpdateProfilePictureWebModel - { - public IFormFile Picture { get; set; } - } -} diff --git a/src/DevHive.Web/Models/Post/Rating/RatePostWebModel.cs b/src/DevHive.Web/Models/Post/Rating/RatePostWebModel.cs deleted file mode 100644 index 5f0e58f..0000000 --- a/src/DevHive.Web/Models/Post/Rating/RatePostWebModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace DevHive.Web.Models.Post.Rating -{ - public class RatePostWebModel - { - public Guid PostId { get; set; } - - public bool Liked { get; set; } - } -} diff --git a/src/DevHive.Web/Models/Post/Rating/ReadPostRatingWebModel.cs b/src/DevHive.Web/Models/Post/Rating/ReadPostRatingWebModel.cs deleted file mode 100644 index a551fb8..0000000 --- a/src/DevHive.Web/Models/Post/Rating/ReadPostRatingWebModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace DevHive.Web.Models.Post.Rating -{ - public class ReadPostRatingWebModel - { - public Guid Id { get; set; } - - public Guid PostId { get; set; } - - public int Likes { get; set; } - - public int Dislikes { get; set; } - } -} diff --git a/src/DevHive.Web/Program.cs b/src/DevHive.Web/Program.cs deleted file mode 100644 index 6982da9..0000000 --- a/src/DevHive.Web/Program.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Hosting;
-
-namespace DevHive.Web
-{
- public class Program
- {
- private const int HTTP_PORT = 5000;
-
- public static void Main(string[] args)
- {
- CreateHostBuilder(args).Build().Run();
- }
-
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.ConfigureKestrel(opt => opt.ListenLocalhost(HTTP_PORT));
- webBuilder.UseStartup<Startup>();
- });
- }
-}
diff --git a/src/DevHive.Web/appsettings.json b/src/DevHive.Web/appsettings.json deleted file mode 100644 index bcdcae7..0000000 --- a/src/DevHive.Web/appsettings.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "AppSettings": { - "Secret": "gXfQlU6qpDleFWyimscjYcT3tgFsQg3yoFjcvSLxG56n1Vu2yptdIUq254wlJWjm" - }, - "ConnectionStrings": { - "DEV": "Server=localhost;Port=5432;Database=API;User Id=postgres;Password=;" - }, - "Cloud": { - "cloudName": "devhive", - "apiKey": "488664116365813", - "apiSecret": "" - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/src/DevHive.code-workspace b/src/DevHive.code-workspace index bb5938b..72c2301 100644 --- a/src/DevHive.code-workspace +++ b/src/DevHive.code-workspace @@ -1,29 +1,21 @@ { "folders": [ { - "name": "DevHive.Web", - "path": "./DevHive.Web" + "name": "Web", + "path": "./Web" }, { - "name": "DevHive.Services", - "path": "./DevHive.Services" + "name": "Services", + "path": "./Services" }, { - "name": "DevHive.Data", - "path": "./DevHive.Data" + "name": "Data", + "path": "./Data" }, { - "name": "DevHive.Common", - "path": "./DevHive.Common" - }, - { - "name": "DevHive.Tests", - "path": "./DevHive.Tests" - }, - { - "name": "DevHive.Angular", - "path": "./DevHive.Angular" - }, + "name": "Common", + "path": "./Common" + } ], "settings": { "files.exclude": { @@ -31,17 +23,14 @@ "**/bin": true, "**/obj": true, - ".gitignore" : true, - - "**/node_modules" : true, - "e2e" : true, + ".gitignore": true }, "code-runner.fileDirectoryAsCwd": true, "dotnet-test-explorer.runInParallel": true, "dotnet-test-explorer.testProjectPath": "**/*.Tests.csproj", "omnisharp.enableEditorConfigSupport": true, "omnisharp.enableRoslynAnalyzers": true, - "prettier.useEditorConfig": true, + "prettier.useEditorConfig": true }, "launch": { "configurations": [ @@ -50,25 +39,25 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "workspace-build", - "program": "${workspaceFolder:DevHive.Web}/bin/Debug/net5.0/DevHive.Web.dll", + "program": "${workspaceFolder:Web}/DevHive.Web/bin/Debug/net5.0/DevHive.Web.dll", "args": [], - "cwd": "${workspaceFolder:DevHive.Web}", + "cwd": "${workspaceFolder:Web}/DevHive.Web", "stopAtEntry": false, "env": { "ASPNETCORE_ENVIRONMENT": "Development" - }, - }, - { - "name": "Launch Data Tests", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "workspace-build", - "program": "${workspaceFolder:DevHive.Tests}/DevHive.Data.Tests/bin/Debug/net5.0/DevHive.Data.Tests.dll", - "args": [], - "cwd": "${workspaceFolder:DevHive.Tests}/DevHive.Data.Tests", - "console": "internalConsole", - "stopAtEntry": false - }, + } + } + // { + // "name": "Launch Data Tests", + // "type": "coreclr", + // "request": "launch", + // "preLaunchTask": "workspace-build", + // "program": "${workspaceFolder:DevHive.Tests}/DevHive.Data.Tests/bin/Debug/net5.0/DevHive.Data.Tests.dll", + // "args": [], + // "cwd": "${workspaceFolder:DevHive.Tests}/DevHive.Data.Tests", + // "console": "internalConsole", + // "stopAtEntry": false + // }, ], "compounds": [] }, @@ -79,6 +68,9 @@ "label": "workspace-build", "command": "dotnet", "type": "shell", + "options": { + "cwd": "${workspaceFolder:Web}/DevHive.Web" + }, "args": [ "build", "/property:GenerateFullPaths=true", @@ -97,11 +89,8 @@ }, "extensions": { "recommendations": [ - "esbenp.prettier-vscode" + "esbenp.prettier-vscode", + "editorconfig.editorconfig" ] - }, - "recommendations": [ - "esbenp.prettier-vscode", - "editorconfig.editorconfig" - ] + } } diff --git a/src/DevHive.sln b/src/DevHive.sln index aa58d9c..a202180 100644 --- a/src/DevHive.sln +++ b/src/DevHive.sln @@ -1,28 +1,37 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30717.126
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHive.Data", "DevHive.Data\DevHive.Data.csproj", "{A175F293-9209-46BF-803E-72E39590246F}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Data", "Data", "{0C2AC7A9-AC68-4668-B88E-9370C596F498}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHive.Services", "DevHive.Services\DevHive.Services.csproj", "{4D4EAC98-A72F-4265-9876-3E87453F80AC}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Data", "Data\DevHive.Data\DevHive.Data.csproj", "{70D0903D-C65F-4600-B6F8-F7BD00500A51}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHive.Web", "DevHive.Web\DevHive.Web.csproj", "{FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Data.Models", "Data\DevHive.Data.Models\DevHive.Data.Models.csproj", "{56F85916-3955-4558-8809-376D20902B94}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHive.Common", "DevHive.Common\DevHive.Common.csproj", "{843BF55D-20AC-41E7-922E-209648625D98}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Data.Tests", "Data\DevHive.Data.Tests\DevHive.Data.Tests.csproj", "{F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DevHive.Tests", "DevHive.Tests", "{8ED705F9-7038-472C-B53F-5B1480A74A37}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{7CA79114-C359-4871-BFA7-0EA898B50AE4}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHive.Data.Tests", "DevHive.Tests\DevHive.Data.Tests\DevHive.Data.Tests.csproj", "{346876CE-2C9B-4538-BE82-EA2017F7D405}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Services", "Services\DevHive.Services\DevHive.Services.csproj", "{B5F22590-E3CE-4595-BE48-AA7F1797A6B8}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHive.Services.Tests", "DevHive.Tests\DevHive.Services.Tests\DevHive.Services.Tests.csproj", "{9BBB8A48-C5AF-4F35-925F-3404A74E47F4}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Services.Models", "Services\DevHive.Services.Models\DevHive.Services.Models.csproj", "{2FFF985B-A26F-443D-A159-62ED2FD5A2BC}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevHive.Web.Tests", "DevHive.Tests\DevHive.Web.Tests\DevHive.Web.Tests.csproj", "{2574CDBE-CC99-4BF8-BF7F-34C131788036}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Services.Tests", "Services\DevHive.Services.Tests\DevHive.Services.Tests.csproj", "{6E58003B-E5E8-4AA4-8F70-A9442BBFC110}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2E5E3F99-7936-4F3B-974E-A98000D1564B}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- EndProjectSection
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{768A592D-58EA-4CD3-A053-2E8F2DC7708A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Web", "Web\DevHive.Web\DevHive.Web.csproj", "{A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Web.Models", "Web\DevHive.Web.Models\DevHive.Web.Models.csproj", "{D8C898F7-A0DE-4939-8708-3D4A5C383EFC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Web.Tests", "Web\DevHive.Web.Tests\DevHive.Web.Tests.csproj", "{608273FF-01ED-48B3-B912-66CCDBF5572E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{49B4EAF5-8F45-493F-A25A-7F37DAAE6B1E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Common", "Common\DevHive.Common\DevHive.Common.csproj", "{AAEC0516-A943-449E-A1E8-E0628BFFAA2E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevHive.Common.Models", "Common\DevHive.Common.Models\DevHive.Common.Models.csproj", "{3D63C965-A734-45D6-B75D-AFDCAB511293}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -33,101 +42,154 @@ Global Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A175F293-9209-46BF-803E-72E39590246F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Debug|x64.ActiveCfg = Debug|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Debug|x64.Build.0 = Debug|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Debug|x86.ActiveCfg = Debug|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Debug|x86.Build.0 = Debug|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Release|Any CPU.Build.0 = Release|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Release|x64.ActiveCfg = Release|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Release|x64.Build.0 = Release|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Release|x86.ActiveCfg = Release|Any CPU
- {A175F293-9209-46BF-803E-72E39590246F}.Release|x86.Build.0 = Release|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Debug|x64.ActiveCfg = Debug|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Debug|x64.Build.0 = Debug|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Debug|x86.ActiveCfg = Debug|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Debug|x86.Build.0 = Debug|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Release|Any CPU.Build.0 = Release|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Release|x64.ActiveCfg = Release|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Release|x64.Build.0 = Release|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Release|x86.ActiveCfg = Release|Any CPU
- {4D4EAC98-A72F-4265-9876-3E87453F80AC}.Release|x86.Build.0 = Release|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Debug|x64.ActiveCfg = Debug|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Debug|x64.Build.0 = Debug|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Debug|x86.Build.0 = Debug|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Release|Any CPU.Build.0 = Release|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Release|x64.ActiveCfg = Release|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Release|x64.Build.0 = Release|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Release|x86.ActiveCfg = Release|Any CPU
- {FF82DC4E-B4C8-4B49-AC73-43A26CFC73DA}.Release|x86.Build.0 = Release|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Debug|x64.ActiveCfg = Debug|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Debug|x64.Build.0 = Debug|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Debug|x86.ActiveCfg = Debug|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Debug|x86.Build.0 = Debug|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Release|Any CPU.Build.0 = Release|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Release|x64.ActiveCfg = Release|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Release|x64.Build.0 = Release|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Release|x86.ActiveCfg = Release|Any CPU
- {843BF55D-20AC-41E7-922E-209648625D98}.Release|x86.Build.0 = Release|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Debug|x64.ActiveCfg = Debug|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Debug|x64.Build.0 = Debug|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Debug|x86.ActiveCfg = Debug|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Debug|x86.Build.0 = Debug|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Release|Any CPU.Build.0 = Release|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Release|x64.ActiveCfg = Release|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Release|x64.Build.0 = Release|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Release|x86.ActiveCfg = Release|Any CPU
- {346876CE-2C9B-4538-BE82-EA2017F7D405}.Release|x86.Build.0 = Release|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Debug|x64.ActiveCfg = Debug|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Debug|x64.Build.0 = Debug|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Debug|x86.ActiveCfg = Debug|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Debug|x86.Build.0 = Debug|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Release|Any CPU.Build.0 = Release|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Release|x64.ActiveCfg = Release|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Release|x64.Build.0 = Release|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Release|x86.ActiveCfg = Release|Any CPU
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4}.Release|x86.Build.0 = Release|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Debug|x64.ActiveCfg = Debug|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Debug|x64.Build.0 = Debug|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Debug|x86.ActiveCfg = Debug|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Debug|x86.Build.0 = Debug|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Release|Any CPU.Build.0 = Release|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Release|x64.ActiveCfg = Release|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Release|x64.Build.0 = Release|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Release|x86.ActiveCfg = Release|Any CPU
- {2574CDBE-CC99-4BF8-BF7F-34C131788036}.Release|x86.Build.0 = Release|Any CPU
- EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {346876CE-2C9B-4538-BE82-EA2017F7D405} = {8ED705F9-7038-472C-B53F-5B1480A74A37}
- {9BBB8A48-C5AF-4F35-925F-3404A74E47F4} = {8ED705F9-7038-472C-B53F-5B1480A74A37}
- {2574CDBE-CC99-4BF8-BF7F-34C131788036} = {8ED705F9-7038-472C-B53F-5B1480A74A37}
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Debug|x64.Build.0 = Debug|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Debug|x86.Build.0 = Debug|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Release|Any CPU.Build.0 = Release|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Release|x64.ActiveCfg = Release|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Release|x64.Build.0 = Release|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Release|x86.ActiveCfg = Release|Any CPU
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51}.Release|x86.Build.0 = Release|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Debug|x64.Build.0 = Debug|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Debug|x86.Build.0 = Debug|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Release|Any CPU.Build.0 = Release|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Release|x64.ActiveCfg = Release|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Release|x64.Build.0 = Release|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Release|x86.ActiveCfg = Release|Any CPU
+ {56F85916-3955-4558-8809-376D20902B94}.Release|x86.Build.0 = Release|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Debug|x64.Build.0 = Debug|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Debug|x86.Build.0 = Debug|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Release|x64.ActiveCfg = Release|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Release|x64.Build.0 = Release|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Release|x86.ActiveCfg = Release|Any CPU
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0}.Release|x86.Build.0 = Release|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Debug|x64.Build.0 = Debug|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Debug|x86.Build.0 = Debug|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Release|x64.ActiveCfg = Release|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Release|x64.Build.0 = Release|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Release|x86.ActiveCfg = Release|Any CPU
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8}.Release|x86.Build.0 = Release|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Debug|x64.Build.0 = Debug|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Debug|x86.Build.0 = Debug|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Release|x64.ActiveCfg = Release|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Release|x64.Build.0 = Release|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Release|x86.ActiveCfg = Release|Any CPU
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC}.Release|x86.Build.0 = Release|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Debug|x64.Build.0 = Debug|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Debug|x86.Build.0 = Debug|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Release|x64.ActiveCfg = Release|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Release|x64.Build.0 = Release|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Release|x86.ActiveCfg = Release|Any CPU
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110}.Release|x86.Build.0 = Release|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Debug|x64.Build.0 = Debug|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Debug|x86.Build.0 = Debug|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Release|x64.ActiveCfg = Release|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Release|x64.Build.0 = Release|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Release|x86.ActiveCfg = Release|Any CPU
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A}.Release|x86.Build.0 = Release|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Debug|x64.Build.0 = Debug|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Debug|x86.Build.0 = Debug|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Release|x64.ActiveCfg = Release|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Release|x64.Build.0 = Release|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Release|x86.ActiveCfg = Release|Any CPU
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC}.Release|x86.Build.0 = Release|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Debug|x64.Build.0 = Debug|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Debug|x86.Build.0 = Debug|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Release|x64.ActiveCfg = Release|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Release|x64.Build.0 = Release|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Release|x86.ActiveCfg = Release|Any CPU
+ {608273FF-01ED-48B3-B912-66CCDBF5572E}.Release|x86.Build.0 = Release|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Debug|x64.Build.0 = Debug|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Debug|x86.Build.0 = Debug|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Release|x64.ActiveCfg = Release|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Release|x64.Build.0 = Release|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Release|x86.ActiveCfg = Release|Any CPU
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E}.Release|x86.Build.0 = Release|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Debug|x64.Build.0 = Debug|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Debug|x86.Build.0 = Debug|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Release|x64.ActiveCfg = Release|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Release|x64.Build.0 = Release|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Release|x86.ActiveCfg = Release|Any CPU
+ {3D63C965-A734-45D6-B75D-AFDCAB511293}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {0F5395E8-26FB-40FD-83A1-EE7766C5E398}
+ GlobalSection(NestedProjects) = preSolution
+ {70D0903D-C65F-4600-B6F8-F7BD00500A51} = {0C2AC7A9-AC68-4668-B88E-9370C596F498}
+ {56F85916-3955-4558-8809-376D20902B94} = {0C2AC7A9-AC68-4668-B88E-9370C596F498}
+ {F056B3F1-B72D-4935-87EA-F7BFEA96AFB0} = {0C2AC7A9-AC68-4668-B88E-9370C596F498}
+ {B5F22590-E3CE-4595-BE48-AA7F1797A6B8} = {7CA79114-C359-4871-BFA7-0EA898B50AE4}
+ {2FFF985B-A26F-443D-A159-62ED2FD5A2BC} = {7CA79114-C359-4871-BFA7-0EA898B50AE4}
+ {6E58003B-E5E8-4AA4-8F70-A9442BBFC110} = {7CA79114-C359-4871-BFA7-0EA898B50AE4}
+ {A6D35BD9-A2A4-4937-89A8-DCB0D610B04A} = {768A592D-58EA-4CD3-A053-2E8F2DC7708A}
+ {D8C898F7-A0DE-4939-8708-3D4A5C383EFC} = {768A592D-58EA-4CD3-A053-2E8F2DC7708A}
+ {608273FF-01ED-48B3-B912-66CCDBF5572E} = {768A592D-58EA-4CD3-A053-2E8F2DC7708A}
+ {AAEC0516-A943-449E-A1E8-E0628BFFAA2E} = {49B4EAF5-8F45-493F-A25A-7F37DAAE6B1E}
+ {3D63C965-A734-45D6-B75D-AFDCAB511293} = {49B4EAF5-8F45-493F-A25A-7F37DAAE6B1E}
EndGlobalSection
EndGlobal
diff --git a/src/Dockerfile b/src/Dockerfile new file mode 100644 index 0000000..0491463 --- /dev/null +++ b/src/Dockerfile @@ -0,0 +1,11 @@ +FROM mcr.microsoft.com/dotnet/sdk:5.0 AS sdk + +COPY . ./Build +WORKDIR /Build +RUN [ "dotnet", "publish", "-f", "net5.0", "-c", "Release", "Web/DevHive.Web/DevHive.Web.csproj", "-o", "/Out"] + +FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime +COPY --from=sdk /Out /App + +WORKDIR /App +ENTRYPOINT [ "dotnet", "DevHive.Web.dll" ] diff --git a/src/DevHive.Services/Models/Comment/CreateCommentServiceModel.cs b/src/Services/DevHive.Services.Models/Comment/CreateCommentServiceModel.cs index 30e919b..30e919b 100644 --- a/src/DevHive.Services/Models/Comment/CreateCommentServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Comment/CreateCommentServiceModel.cs diff --git a/src/DevHive.Services/Models/Comment/ReadCommentServiceModel.cs b/src/Services/DevHive.Services.Models/Comment/ReadCommentServiceModel.cs index 3196233..3196233 100644 --- a/src/DevHive.Services/Models/Comment/ReadCommentServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Comment/ReadCommentServiceModel.cs diff --git a/src/DevHive.Services/Models/Comment/UpdateCommentServiceModel.cs b/src/Services/DevHive.Services.Models/Comment/UpdateCommentServiceModel.cs index 3b78200..3b78200 100644 --- a/src/DevHive.Services/Models/Comment/UpdateCommentServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Comment/UpdateCommentServiceModel.cs diff --git a/src/Services/DevHive.Services.Models/DevHive.Services.Models.csproj b/src/Services/DevHive.Services.Models/DevHive.Services.Models.csproj new file mode 100644 index 0000000..2345a8e --- /dev/null +++ b/src/Services/DevHive.Services.Models/DevHive.Services.Models.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + <ItemGroup> + <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\Common\DevHive.Common\DevHive.Common.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common.Models\DevHive.Common.Models.csproj"/> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/DevHive.Services/Models/Feed/GetPageServiceModel.cs b/src/Services/DevHive.Services.Models/Feed/GetPageServiceModel.cs index 1f02486..1f02486 100644 --- a/src/DevHive.Services/Models/Feed/GetPageServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Feed/GetPageServiceModel.cs diff --git a/src/DevHive.Services/Models/Feed/ReadPageServiceModel.cs b/src/Services/DevHive.Services.Models/Feed/ReadPageServiceModel.cs index 95f6845..95f6845 100644 --- a/src/DevHive.Services/Models/Feed/ReadPageServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Feed/ReadPageServiceModel.cs diff --git a/src/DevHive.Services/Models/Language/CreateLanguageServiceModel.cs b/src/Services/DevHive.Services.Models/Language/CreateLanguageServiceModel.cs index 9d66d3e..9d66d3e 100644 --- a/src/DevHive.Services/Models/Language/CreateLanguageServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Language/CreateLanguageServiceModel.cs diff --git a/src/DevHive.Services/Models/Language/LanguageServiceModel.cs b/src/Services/DevHive.Services.Models/Language/LanguageServiceModel.cs index a07aa16..a07aa16 100644 --- a/src/DevHive.Services/Models/Language/LanguageServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Language/LanguageServiceModel.cs diff --git a/src/DevHive.Services/Models/Language/ReadLanguageServiceModel.cs b/src/Services/DevHive.Services.Models/Language/ReadLanguageServiceModel.cs index 651dc6d..651dc6d 100644 --- a/src/DevHive.Services/Models/Language/ReadLanguageServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Language/ReadLanguageServiceModel.cs diff --git a/src/DevHive.Services/Models/Language/UpdateLanguageServiceModel.cs b/src/Services/DevHive.Services.Models/Language/UpdateLanguageServiceModel.cs index 84b7f27..84b7f27 100644 --- a/src/DevHive.Services/Models/Language/UpdateLanguageServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Language/UpdateLanguageServiceModel.cs diff --git a/src/DevHive.Services/Models/Post/CreatePostServiceModel.cs b/src/Services/DevHive.Services.Models/Post/CreatePostServiceModel.cs index 304eb90..304eb90 100644 --- a/src/DevHive.Services/Models/Post/CreatePostServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Post/CreatePostServiceModel.cs diff --git a/src/DevHive.Services/Models/Post/ReadPostServiceModel.cs b/src/Services/DevHive.Services.Models/Post/ReadPostServiceModel.cs index a7aa882..33d6520 100644 --- a/src/DevHive.Services/Models/Post/ReadPostServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Post/ReadPostServiceModel.cs @@ -21,6 +21,7 @@ namespace DevHive.Services.Models.Post public List<IdModel> Comments { get; set; } = new(); public List<string> FileUrls { get; set; } - // public List<FileContentResult> Files { get; set; } = new(); + + public int CurrentRating { get; set; } } } diff --git a/src/DevHive.Services/Models/Post/UpdatePostServiceModel.cs b/src/Services/DevHive.Services.Models/Post/UpdatePostServiceModel.cs index 51b16bc..51b16bc 100644 --- a/src/DevHive.Services/Models/Post/UpdatePostServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Post/UpdatePostServiceModel.cs diff --git a/src/Services/DevHive.Services.Models/ProfilePicture/ProfilePictureServiceModel.cs b/src/Services/DevHive.Services.Models/ProfilePicture/ProfilePictureServiceModel.cs new file mode 100644 index 0000000..5e69d13 --- /dev/null +++ b/src/Services/DevHive.Services.Models/ProfilePicture/ProfilePictureServiceModel.cs @@ -0,0 +1,11 @@ +using System; +using Microsoft.AspNetCore.Http; + +namespace DevHive.Services.Models.ProfilePicture +{ + public class ProfilePictureServiceModel + { + public Guid UserId { get; set; } + public IFormFile ProfilePictureFormFile { get; set; } + } +} diff --git a/src/Services/DevHive.Services.Models/Rating/CreateRatingServiceModel.cs b/src/Services/DevHive.Services.Models/Rating/CreateRatingServiceModel.cs new file mode 100644 index 0000000..486e3d2 --- /dev/null +++ b/src/Services/DevHive.Services.Models/Rating/CreateRatingServiceModel.cs @@ -0,0 +1,13 @@ +using System; + +namespace DevHive.Services.Models.Rating +{ + public class CreateRatingServiceModel + { + public Guid UserId { get; set; } + + public Guid PostId { get; set; } + + public bool IsLike { get; set; } + } +} diff --git a/src/Services/DevHive.Services.Models/Rating/ReadRatingServiceModel.cs b/src/Services/DevHive.Services.Models/Rating/ReadRatingServiceModel.cs new file mode 100644 index 0000000..56a5ab0 --- /dev/null +++ b/src/Services/DevHive.Services.Models/Rating/ReadRatingServiceModel.cs @@ -0,0 +1,15 @@ +using System; + +namespace DevHive.Services.Models.Rating +{ + public class ReadRatingServiceModel + { + public Guid Id { get; set; } + + public Guid PostId { get; set; } + + public Guid UserId { get; set; } + + public bool IsLike { get; set; } + } +} diff --git a/src/Services/DevHive.Services.Models/Rating/UpdateRatingServiceModel.cs b/src/Services/DevHive.Services.Models/Rating/UpdateRatingServiceModel.cs new file mode 100644 index 0000000..923e789 --- /dev/null +++ b/src/Services/DevHive.Services.Models/Rating/UpdateRatingServiceModel.cs @@ -0,0 +1,15 @@ +using System; + +namespace DevHive.Services.Models.Rating +{ + public class UpdateRatingServiceModel + { + public Guid Id { get; set; } + + public Guid UserId { get; set; } + + public Guid PostId { get; set; } + + public bool IsLike { get; set; } + } +} diff --git a/src/DevHive.Services/Models/Identity/Role/CreateRoleServiceModel.cs b/src/Services/DevHive.Services.Models/Role/CreateRoleServiceModel.cs index 3bed3fd..8b03c65 100644 --- a/src/DevHive.Services/Models/Identity/Role/CreateRoleServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Role/CreateRoleServiceModel.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -namespace DevHive.Services.Models.Identity.Role +namespace DevHive.Services.Models.Role { public class CreateRoleServiceModel { diff --git a/src/DevHive.Services/Models/Identity/Role/RoleServiceModel.cs b/src/Services/DevHive.Services.Models/Role/RoleServiceModel.cs index 07249fe..77ca954 100644 --- a/src/DevHive.Services/Models/Identity/Role/RoleServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Role/RoleServiceModel.cs @@ -1,4 +1,4 @@ -namespace DevHive.Services.Models.Identity.Role +namespace DevHive.Services.Models.Role { public class RoleServiceModel { diff --git a/src/DevHive.Services/Models/Identity/Role/UpdateRoleServiceModel.cs b/src/Services/DevHive.Services.Models/Role/UpdateRoleServiceModel.cs index e21e6b4..9b8fbfe 100644 --- a/src/DevHive.Services/Models/Identity/Role/UpdateRoleServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Role/UpdateRoleServiceModel.cs @@ -1,6 +1,6 @@ using System; -namespace DevHive.Services.Models.Identity.Role +namespace DevHive.Services.Models.Role { public class UpdateRoleServiceModel { diff --git a/src/DevHive.Services/Models/Technology/CreateTechnologyServiceModel.cs b/src/Services/DevHive.Services.Models/Technology/CreateTechnologyServiceModel.cs index a31d160..a31d160 100644 --- a/src/DevHive.Services/Models/Technology/CreateTechnologyServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Technology/CreateTechnologyServiceModel.cs diff --git a/src/DevHive.Services/Models/Technology/ReadTechnologyServiceModel.cs b/src/Services/DevHive.Services.Models/Technology/ReadTechnologyServiceModel.cs index 99f4750..99f4750 100644 --- a/src/DevHive.Services/Models/Technology/ReadTechnologyServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Technology/ReadTechnologyServiceModel.cs diff --git a/src/DevHive.Services/Models/Technology/TechnologyServiceModel.cs b/src/Services/DevHive.Services.Models/Technology/TechnologyServiceModel.cs index cb5c881..cb5c881 100644 --- a/src/DevHive.Services/Models/Technology/TechnologyServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Technology/TechnologyServiceModel.cs diff --git a/src/DevHive.Services/Models/Technology/UpdateTechnologyServiceModel.cs b/src/Services/DevHive.Services.Models/Technology/UpdateTechnologyServiceModel.cs index f4c7921..f4c7921 100644 --- a/src/DevHive.Services/Models/Technology/UpdateTechnologyServiceModel.cs +++ b/src/Services/DevHive.Services.Models/Technology/UpdateTechnologyServiceModel.cs diff --git a/src/DevHive.Services/Models/Identity/User/BaseUserServiceModel.cs b/src/Services/DevHive.Services.Models/User/BaseUserServiceModel.cs index 514f82a..5c3dc6b 100644 --- a/src/DevHive.Services/Models/Identity/User/BaseUserServiceModel.cs +++ b/src/Services/DevHive.Services.Models/User/BaseUserServiceModel.cs @@ -1,4 +1,4 @@ -namespace DevHive.Services.Models.Identity.User +namespace DevHive.Services.Models.User { public class BaseUserServiceModel { diff --git a/src/DevHive.Services/Models/Identity/User/FriendServiceModel.cs b/src/Services/DevHive.Services.Models/User/FriendServiceModel.cs index a784f5c..2241886 100644 --- a/src/DevHive.Services/Models/Identity/User/FriendServiceModel.cs +++ b/src/Services/DevHive.Services.Models/User/FriendServiceModel.cs @@ -1,6 +1,6 @@ using System; -namespace DevHive.Services.Models.Identity.User +namespace DevHive.Services.Models.User { public class FriendServiceModel { diff --git a/src/DevHive.Services/Models/Identity/User/LoginServiceModel.cs b/src/Services/DevHive.Services.Models/User/LoginServiceModel.cs index 4d53283..6a0d128 100644 --- a/src/DevHive.Services/Models/Identity/User/LoginServiceModel.cs +++ b/src/Services/DevHive.Services.Models/User/LoginServiceModel.cs @@ -1,4 +1,4 @@ -namespace DevHive.Services.Models.Identity.User +namespace DevHive.Services.Models.User { public class LoginServiceModel { diff --git a/src/DevHive.Services/Models/Identity/User/RegisterServiceModel.cs b/src/Services/DevHive.Services.Models/User/RegisterServiceModel.cs index adc4119..5852d46 100644 --- a/src/DevHive.Services/Models/Identity/User/RegisterServiceModel.cs +++ b/src/Services/DevHive.Services.Models/User/RegisterServiceModel.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using DevHive.Services.Models.Language; using DevHive.Services.Models.Technology; -namespace DevHive.Services.Models.Identity.User +namespace DevHive.Services.Models.User { public class RegisterServiceModel : BaseUserServiceModel { diff --git a/src/DevHive.Services/Models/Identity/User/UpdateFriendServiceModel.cs b/src/Services/DevHive.Services.Models/User/UpdateFriendServiceModel.cs index b0efe10..0d7a8c1 100644 --- a/src/DevHive.Services/Models/Identity/User/UpdateFriendServiceModel.cs +++ b/src/Services/DevHive.Services.Models/User/UpdateFriendServiceModel.cs @@ -1,6 +1,6 @@ using System; -namespace DevHive.Services.Models.Identity.User +namespace DevHive.Services.Models.User { public class UpdateFriendServiceModel { diff --git a/src/DevHive.Services/Models/Identity/User/UpdateUserServiceModel.cs b/src/Services/DevHive.Services.Models/User/UpdateUserServiceModel.cs index b4e4400..ff20e6b 100644 --- a/src/DevHive.Services/Models/Identity/User/UpdateUserServiceModel.cs +++ b/src/Services/DevHive.Services.Models/User/UpdateUserServiceModel.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; -using DevHive.Services.Models.Identity.Role; +using DevHive.Services.Models.Role; using DevHive.Services.Models.Language; using DevHive.Services.Models.Technology; -namespace DevHive.Services.Models.Identity.User +namespace DevHive.Services.Models.User { public class UpdateUserServiceModel : BaseUserServiceModel { diff --git a/src/DevHive.Services/Models/Identity/User/UserServiceModel.cs b/src/Services/DevHive.Services.Models/User/UserServiceModel.cs index ac7bba2..f48c703 100644 --- a/src/DevHive.Services/Models/Identity/User/UserServiceModel.cs +++ b/src/Services/DevHive.Services.Models/User/UserServiceModel.cs @@ -1,14 +1,14 @@ using System.Collections.Generic; using DevHive.Common.Models.Misc; -using DevHive.Services.Models.Identity.Role; +using DevHive.Services.Models.Role; using DevHive.Services.Models.Language; using DevHive.Services.Models.Technology; -namespace DevHive.Services.Models.Identity.User +namespace DevHive.Services.Models.User { public class UserServiceModel : BaseUserServiceModel { - public string ProfilePictureURL { get; set; } + public string ProfilePictureURL { get; set; } = new(string.Empty); public HashSet<RoleServiceModel> Roles { get; set; } = new(); diff --git a/src/Services/DevHive.Services.Tests/CommentService.Tests.cs b/src/Services/DevHive.Services.Tests/CommentService.Tests.cs new file mode 100644 index 0000000..12229e8 --- /dev/null +++ b/src/Services/DevHive.Services.Tests/CommentService.Tests.cs @@ -0,0 +1,314 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Models.Comment; +using DevHive.Services.Services; +using Moq; +using NUnit.Framework; + +namespace DevHive.Services.Tests +{ + [TestFixture] + public class CommentServiceTests + { + private const string MESSAGE = "Gosho Trapov"; + private Mock<IUserRepository> _userRepositoryMock; + private Mock<IPostRepository> _postRepositoryMock; + private Mock<ICommentRepository> _commentRepositoryMock; + private Mock<IMapper> _mapperMock; + private CommentService _commentService; + + #region Setup + [SetUp] + public void Setup() + { + this._userRepositoryMock = new Mock<IUserRepository>(); + this._postRepositoryMock = new Mock<IPostRepository>(); + this._commentRepositoryMock = new Mock<ICommentRepository>(); + this._mapperMock = new Mock<IMapper>(); + this._commentService = new CommentService(this._userRepositoryMock.Object, this._postRepositoryMock.Object, this._commentRepositoryMock.Object, this._mapperMock.Object); + } + #endregion + + #region AddComment + [Test] + public async Task AddComment_ReturnsNonEmptyGuid_WhenEntityIsAddedSuccessfully() + { + Guid id = Guid.NewGuid(); + User creator = new() { Id = Guid.NewGuid() }; + CreateCommentServiceModel createCommentServiceModel = new() + { + Message = MESSAGE + }; + Comment comment = new() + { + Message = MESSAGE, + Id = id, + }; + + this._commentRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Comment>())) + .ReturnsAsync(true); + this._commentRepositoryMock + .Setup(p => p.GetCommentByIssuerAndTimeCreatedAsync(It.IsAny<Guid>(), It.IsAny<DateTime>())) + .ReturnsAsync(comment); + this._postRepositoryMock + .Setup(p => p.DoesPostExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(creator); + this._mapperMock + .Setup(p => p.Map<Comment>(It.IsAny<CreateCommentServiceModel>())) + .Returns(comment); + + Guid result = await this._commentService.AddComment(createCommentServiceModel); + + Assert.AreEqual(id, result); + } + + [Test] + public async Task AddComment_ReturnsEmptyGuid_WhenEntityIsNotAddedSuccessfully() + { + CreateCommentServiceModel createCommentServiceModel = new() + { + Message = MESSAGE + }; + Comment comment = new() + { + Message = MESSAGE, + }; + + this._commentRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Comment>())) + .ReturnsAsync(false); + this._postRepositoryMock + .Setup(p => p.DoesPostExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<Comment>(It.IsAny<CreateCommentServiceModel>())) + .Returns(comment); + + Guid result = await this._commentService.AddComment(createCommentServiceModel); + + Assert.IsTrue(result == Guid.Empty); + } + + [Test] + public void AddComment_ThrowsException_WhenPostDoesNotExist() + { + const string EXCEPTION_MESSAGE = "Post does not exist!"; + + CreateCommentServiceModel createCommentServiceModel = new() + { + Message = MESSAGE + }; + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._commentService.AddComment(createCommentServiceModel), "AddComment does not throw excpeion when the post does not exist"); + + // Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorecct exception message"); + } + #endregion + + #region GetCommentById + [Test] + public async Task GetCommentById_ReturnsTheComment_WhenItExists() + { + Guid creatorId = new(); + User creator = new() { Id = creatorId }; + Comment comment = new() + { + Message = MESSAGE, + Creator = creator + }; + ReadCommentServiceModel commentServiceModel = new() + { + Message = MESSAGE + }; + User user = new() + { + Id = creatorId, + }; + + this._commentRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(comment); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(user); + this._mapperMock + .Setup(p => p.Map<ReadCommentServiceModel>(It.IsAny<Comment>())) + .Returns(commentServiceModel); + + ReadCommentServiceModel result = await this._commentService.GetCommentById(Guid.NewGuid()); + + Assert.AreEqual(MESSAGE, result.Message); + } + + [Test] + public void GetCommentById_ThorwsException_WhenTheUserDoesNotExist() + { + const string EXCEPTION_MESSAGE = "The user does not exist"; + Guid creatorId = new(); + User creator = new() { Id = creatorId }; + Comment comment = new() + { + Message = MESSAGE, + Creator = creator + }; + + this._commentRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(comment); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._commentService.GetCommentById(Guid.NewGuid()), "GetCommentById does not throw exception when the user does not exist"); + + // Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message); + } + + [Test] + public void GetCommentById_ThrowsException_WhenCommentDoesNotExist() + { + string exceptionMessage = "The comment does not exist"; + Guid creatorId = new(); + User user = new() + { + Id = creatorId, + }; + + this._commentRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .Returns(Task.FromResult<Comment>(null)); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(user); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._commentService.GetCommentById(Guid.NewGuid())); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region UpdateComment + [Test] + public async Task UpdateComment_ReturnsTheIdOfTheComment_WhenUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + Comment comment = new() + { + Id = id, + Message = MESSAGE + }; + UpdateCommentServiceModel updateCommentServiceModel = new() + { + CommentId = id, + NewMessage = MESSAGE + }; + + this._commentRepositoryMock + .Setup(p => p.DoesCommentExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._commentRepositoryMock + .Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Comment>())) + .ReturnsAsync(true); + this._commentRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(comment); + this._mapperMock + .Setup(p => p.Map<Comment>(It.IsAny<UpdateCommentServiceModel>())) + .Returns(comment); + + Guid result = await this._commentService.UpdateComment(updateCommentServiceModel); + + Assert.AreEqual(updateCommentServiceModel.CommentId, result); + } + + [Test] + public async Task UpdateComment_ReturnsEmptyId_WhenTheCommentIsNotUpdatedSuccessfully() + { + Comment comment = new() + { + Message = MESSAGE + }; + UpdateCommentServiceModel updateCommentServiceModel = new() + { + CommentId = Guid.NewGuid(), + NewMessage = MESSAGE + }; + + this._commentRepositoryMock + .Setup(p => p.DoesCommentExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._commentRepositoryMock + .Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Comment>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<Comment>(It.IsAny<UpdateCommentServiceModel>())) + .Returns(comment); + + Guid result = await this._commentService.UpdateComment(updateCommentServiceModel); + + Assert.AreEqual(Guid.Empty, result); + } + + [Test] + public void UpdateComment_ThrowsArgumentException_WhenCommentDoesNotExist() + { + string exceptionMessage = "Comment does not exist!"; + UpdateCommentServiceModel updateCommentServiceModel = new() + { + }; + + this._commentRepositoryMock + .Setup(p => p.DoesCommentExist(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._commentService.UpdateComment(updateCommentServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region DeleteComment + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task DeleteComment_ShouldReturnIfDeletionIsSuccessfull_WhenCommentExists(bool shouldPass) + { + Guid id = new(); + Comment comment = new(); + + this._commentRepositoryMock + .Setup(p => p.DoesCommentExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._commentRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(comment); + this._commentRepositoryMock + .Setup(p => p.DeleteAsync(It.IsAny<Comment>())) + .ReturnsAsync(shouldPass); + + bool result = await this._commentService.DeleteComment(id); + + Assert.AreEqual(shouldPass, result); + } + + [Test] + public void DeleteComment_ThrowsException_WhenCommentDoesNotExist() + { + string exceptionMessage = "Comment does not exist!"; + Guid id = new(); + + this._commentRepositoryMock + .Setup(p => p.DoesCommentExist(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._commentService.DeleteComment(id)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorrect exception message"); + } + #endregion + } +} diff --git a/src/DevHive.Tests/DevHive.Services.Tests/DevHive.Services.Tests.csproj b/src/Services/DevHive.Services.Tests/DevHive.Services.Tests.csproj index ce5cb62..4a7237b 100644 --- a/src/DevHive.Tests/DevHive.Services.Tests/DevHive.Services.Tests.csproj +++ b/src/Services/DevHive.Services.Tests/DevHive.Services.Tests.csproj @@ -1,25 +1,23 @@ <Project Sdk="Microsoft.NET.Sdk"> - <PropertyGroup> <TargetFramework>net5.0</TargetFramework> - <IsPackable>false</IsPackable> </PropertyGroup> - <ItemGroup> - <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.1" /> - <PackageReference Include="Moq" Version="4.16.0" /> - <PackageReference Include="NUnit" Version="3.13.0" /> - <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.4"/> + <PackageReference Include="Moq" Version="4.16.1"/> + <PackageReference Include="NUnit" Version="3.13.1"/> + <PackageReference Include="NUnit3TestAdapter" Version="3.17.0"/> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\..\DevHive.Services\DevHive.Services.csproj" /> + <ProjectReference Include="..\DevHive.Services\DevHive.Services.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common\DevHive.Common.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common.Models\DevHive.Common.Models.csproj"/> </ItemGroup> - <PropertyGroup> <EnableNETAnalyzers>true</EnableNETAnalyzers> <AnalysisLevel>latest</AnalysisLevel> </PropertyGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/src/DevHive.Tests/DevHive.Services.Tests/FeedService.Tests.cs b/src/Services/DevHive.Services.Tests/FeedService.Tests.cs index e4020c5..1f21394 100644 --- a/src/DevHive.Tests/DevHive.Services.Tests/FeedService.Tests.cs +++ b/src/Services/DevHive.Services.Tests/FeedService.Tests.cs @@ -2,7 +2,7 @@ //using System.Collections.Generic; //using System.Threading.Tasks; //using AutoMapper; -//using DevHive.Data.Interfaces.Repositories; +//using DevHive.Data.Interfaces; //using DevHive.Data.Models; //using DevHive.Services.Models; //using DevHive.Services.Models.Post; diff --git a/src/Services/DevHive.Services.Tests/LanguageService.Tests.cs b/src/Services/DevHive.Services.Tests/LanguageService.Tests.cs new file mode 100644 index 0000000..eefeaea --- /dev/null +++ b/src/Services/DevHive.Services.Tests/LanguageService.Tests.cs @@ -0,0 +1,315 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Models.Language; +using DevHive.Services.Services; +using Moq; +using NUnit.Framework; + +namespace DevHive.Services.Tests +{ + [TestFixture] + public class LanguageServiceTests + { + private Mock<ILanguageRepository> _languageRepositoryMock; + private Mock<IMapper> _mapperMock; + private LanguageService _languageService; + + #region SetUps + [SetUp] + public void SetUp() + { + this._languageRepositoryMock = new Mock<ILanguageRepository>(); + this._mapperMock = new Mock<IMapper>(); + this._languageService = new LanguageService(this._languageRepositoryMock.Object, this._mapperMock.Object); + } + #endregion + + #region CreateLanguage + [Test] + public async Task CreateLanguage_ReturnsNonEmptyGuid_WhenEntityIsAddedSuccessfully() + { + string technologyName = "Gosho Trapov"; + Guid id = Guid.NewGuid(); + CreateLanguageServiceModel createLanguageServiceModel = new CreateLanguageServiceModel + { + Name = technologyName + }; + Language language = new Language + { + Name = technologyName, + Id = id + }; + + this._languageRepositoryMock + .Setup(p => p.DoesLanguageNameExistAsync(It.IsAny<string>())) + .ReturnsAsync(false); + this._languageRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Language>())) + .ReturnsAsync(true); + this._languageRepositoryMock + .Setup(p => p.GetByNameAsync(It.IsAny<string>())) + .ReturnsAsync(language); + this._mapperMock + .Setup(p => p.Map<Language>(It.IsAny<CreateLanguageServiceModel>())) + .Returns(language); + + Guid result = await this._languageService.CreateLanguage(createLanguageServiceModel); + + Assert.AreEqual(id, result); + } + + [Test] + public async Task CreateLanguage_ReturnsEmptyGuid_WhenEntityIsNotAddedSuccessfully() + { + string languageName = "Gosho Trapov"; + + CreateLanguageServiceModel createLanguageServiceModel = new CreateLanguageServiceModel + { + Name = languageName + }; + Language language = new Language + { + Name = languageName + }; + + this._languageRepositoryMock + .Setup(p => p.DoesLanguageNameExistAsync(It.IsAny<string>())) + .ReturnsAsync(false); + this._languageRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Language>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<Language>(It.IsAny<CreateLanguageServiceModel>())) + .Returns(language); + + Guid result = await this._languageService.CreateLanguage(createLanguageServiceModel); + + Assert.IsTrue(result == Guid.Empty); + + } + + [Test] + public void CreateLanguage_ThrowsArgumentException_WhenEntityAlreadyExists() + { + string exceptionMessage = "Language already exists!"; + string languageName = "Gosho Trapov"; + + CreateLanguageServiceModel createLanguageServiceModel = new CreateLanguageServiceModel + { + Name = languageName + }; + Language language = new Language + { + Name = languageName + }; + + this._languageRepositoryMock + .Setup(p => p.DoesLanguageNameExistAsync(It.IsAny<string>())) + .ReturnsAsync(true); + + Exception ex = Assert.ThrowsAsync<DuplicateNameException>(() => this._languageService.CreateLanguage(createLanguageServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region GetLanguageById + [Test] + public async Task GetLanguageById_ReturnsTheLanguage_WhenItExists() + { + Guid id = Guid.NewGuid(); + string name = "Gosho Trapov"; + Language language = new Language + { + Name = name + }; + ReadLanguageServiceModel readLanguageServiceModel = new ReadLanguageServiceModel + { + Name = name + }; + + this._languageRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(language); + this._mapperMock + .Setup(p => p.Map<ReadLanguageServiceModel>(It.IsAny<Language>())) + .Returns(readLanguageServiceModel); + + ReadLanguageServiceModel result = await this._languageService.GetLanguageById(id); + + Assert.AreEqual(name, result.Name); + } + + [Test] + public void GetLanguageById_ThrowsException_WhenLanguageDoesNotExist() + { + string exceptionMessage = "The language does not exist"; + Guid id = Guid.NewGuid(); + this._languageRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .Returns(Task.FromResult<Language>(null)); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._languageService.GetLanguageById(id)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region GetLanguages + [Test] + public void GetLanguages_ReturnsAllLanguages_IfAnyExist() + { + ReadLanguageServiceModel firstLanguage = new ReadLanguageServiceModel(); + ReadLanguageServiceModel secondLanguage = new ReadLanguageServiceModel(); + HashSet<ReadLanguageServiceModel> languges = new HashSet<ReadLanguageServiceModel>(); + languges.Add(firstLanguage); + languges.Add(secondLanguage); + + this._languageRepositoryMock + .Setup(p => p.GetLanguages()) + .Returns(new HashSet<Language>()); + this._mapperMock + .Setup(p => p.Map<HashSet<ReadLanguageServiceModel>>(It.IsAny<HashSet<Language>>())) + .Returns(languges); + + HashSet<ReadLanguageServiceModel> result = this._languageService.GetLanguages(); + + Assert.GreaterOrEqual(2, result.Count, "GetLanguages does not return all languages"); + } + + [Test] + public void GetLanguages_ReturnsEmptyHashSet_IfNoLanguagesExist() + { + this._languageRepositoryMock + .Setup(p => p.GetLanguages()) + .Returns(new HashSet<Language>()); + this._mapperMock + .Setup(p => p.Map<HashSet<ReadLanguageServiceModel>>(It.IsAny<HashSet<Language>>())) + .Returns(new HashSet<ReadLanguageServiceModel>()); + + HashSet<ReadLanguageServiceModel> result = this._languageService.GetLanguages(); + + Assert.IsEmpty(result, "GetLanguages does not return empty string when no languages exist"); + } + #endregion + + #region UpdateLanguage + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task UpdateLanguage_ReturnsIfUpdateIsSuccessfull_WhenLanguageExistsy(bool shouldPass) + { + string name = "Gosho Trapov"; + Guid id = Guid.NewGuid(); + Language language = new Language + { + Name = name, + Id = id + }; + UpdateLanguageServiceModel updateLanguageServiceModel = new UpdateLanguageServiceModel + { + Name = name, + }; + + this._languageRepositoryMock + .Setup(p => p.DoesLanguageExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._languageRepositoryMock + .Setup(p => p.DoesLanguageNameExistAsync(It.IsAny<string>())) + .ReturnsAsync(false); + this._languageRepositoryMock + .Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Language>())) + .ReturnsAsync(shouldPass); + this._mapperMock + .Setup(p => p.Map<Language>(It.IsAny<UpdateLanguageServiceModel>())) + .Returns(language); + + bool result = await this._languageService.UpdateLanguage(updateLanguageServiceModel); + + Assert.AreEqual(shouldPass, result); + } + + [Test] + public void UpdateLanguage_ThrowsArgumentException_WhenLanguageDoesNotExist() + { + string exceptionMessage = "Language does not exist!"; + UpdateLanguageServiceModel updateTechnologyServiceModel = new UpdateLanguageServiceModel + { + }; + + this._languageRepositoryMock + .Setup(p => p.DoesLanguageExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<DuplicateNameException>(() => this._languageService.UpdateLanguage(updateTechnologyServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + + [Test] + public void UpdateLanguage_ThrowsException_WhenLanguageNameAlreadyExists() + { + string exceptionMessage = "Language name already exists in our data base!"; + UpdateLanguageServiceModel updateTechnologyServiceModel = new UpdateLanguageServiceModel + { + }; + + this._languageRepositoryMock + .Setup(p => p.DoesLanguageExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._languageRepositoryMock + .Setup(p => p.DoesLanguageNameExistAsync(It.IsAny<string>())) + .ReturnsAsync(true); + + Exception ex = Assert.ThrowsAsync<DuplicateNameException>(() => this._languageService.UpdateLanguage(updateTechnologyServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region DeleteLanguage + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task DeleteLanguage_ShouldReturnIfDeletionIsSuccessfull_WhenLanguageExists(bool shouldPass) + { + Guid id = Guid.NewGuid(); + Language language = new Language(); + + this._languageRepositoryMock + .Setup(p => p.DoesLanguageExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._languageRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(language); + this._languageRepositoryMock + .Setup(p => p.DeleteAsync(It.IsAny<Language>())) + .ReturnsAsync(shouldPass); + + bool result = await this._languageService.DeleteLanguage(id); + + Assert.AreEqual(shouldPass, result); + } + + [Test] + public void DeleteLanguage_ThrowsException_WhenLanguageDoesNotExist() + { + string exceptionMessage = "Language does not exist!"; + Guid id = Guid.NewGuid(); + + this._languageRepositoryMock + .Setup(p => p.DoesLanguageExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._languageService.DeleteLanguage(id)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + } +} diff --git a/src/Services/DevHive.Services.Tests/PostService.Tests.cs b/src/Services/DevHive.Services.Tests/PostService.Tests.cs new file mode 100644 index 0000000..d534511 --- /dev/null +++ b/src/Services/DevHive.Services.Tests/PostService.Tests.cs @@ -0,0 +1,320 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Post; +using DevHive.Services.Services; +using Microsoft.AspNetCore.Http; +using Moq; +using NUnit.Framework; + +namespace DevHive.Services.Tests +{ + [TestFixture] + public class PostServiceTests + { + private const string MESSAGE = "Gosho Trapov"; + private Mock<ICloudService> _cloudServiceMock; + private Mock<IPostRepository> _postRepositoryMock; + private Mock<ICommentRepository> _commentRepositoryMock; + private Mock<IUserRepository> _userRepositoryMock; + private Mock<IMapper> _mapperMock; + private PostService _postService; + + #region SetUps + [SetUp] + public void Setup() + { + this._postRepositoryMock = new Mock<IPostRepository>(); + this._cloudServiceMock = new Mock<ICloudService>(); + this._userRepositoryMock = new Mock<IUserRepository>(); + this._commentRepositoryMock = new Mock<ICommentRepository>(); + this._mapperMock = new Mock<IMapper>(); + this._postService = new PostService(this._cloudServiceMock.Object, this._userRepositoryMock.Object, this._postRepositoryMock.Object, this._commentRepositoryMock.Object, this._mapperMock.Object); + } + #endregion + + #region CreatePost + [Test] + public async Task CreatePost_ReturnsIdOfThePost_WhenItIsSuccessfullyCreated() + { + Guid postId = Guid.NewGuid(); + User creator = new User { Id = Guid.NewGuid() }; + CreatePostServiceModel createPostServiceModel = new CreatePostServiceModel + { + Files = new List<IFormFile>() + }; + Post post = new Post + { + Message = MESSAGE, + Id = postId, + }; + + this._postRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Post>())) + .ReturnsAsync(true); + this._postRepositoryMock + .Setup(p => p.GetPostByCreatorAndTimeCreatedAsync(It.IsAny<Guid>(), It.IsAny<DateTime>())) + .ReturnsAsync(post); + this._userRepositoryMock + .Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(creator); + this._mapperMock + .Setup(p => p.Map<Post>(It.IsAny<CreatePostServiceModel>())) + .Returns(post); + + Guid result = await this._postService.CreatePost(createPostServiceModel); + + Assert.AreEqual(postId, result, "CreatePost does not return the correct id"); + } + + [Test] + public async Task CreatePost_ReturnsEmptyGuid_WhenItIsNotSuccessfullyCreated() + { + CreatePostServiceModel createPostServiceModel = new CreatePostServiceModel + { + Files = new List<IFormFile>() + }; + Post post = new Post + { + Message = MESSAGE, + }; + + this._postRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Post>())) + .ReturnsAsync(false); + this._userRepositoryMock + .Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<Post>(It.IsAny<CreatePostServiceModel>())) + .Returns(post); + + Guid result = await this._postService.CreatePost(createPostServiceModel); + + Assert.AreEqual(Guid.Empty, result, "CreatePost does not return empty id"); + } + + [Test] + public void CreatePost_ThrowsException_WhenUserDoesNotExist() + { + const string EXCEPTION_MESSAGE = "User does not exist!"; + CreatePostServiceModel createPostServiceModel = new CreatePostServiceModel + { + }; + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._postService.CreatePost(createPostServiceModel), "CreatePost does not throw excpeion when the user does not exist"); + + // Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Excapetion message is not correct"); + } + #endregion + + #region GetPostById + [Test] + public async Task GetPostById_ReturnsThePost_WhenItExists() + { + Guid creatorId = Guid.NewGuid(); + User creator = new User { Id = creatorId }; + Post post = new Post + { + Message = MESSAGE, + Creator = creator, + Ratings = new List<Rating>() + }; + ReadPostServiceModel readPostServiceModel = new ReadPostServiceModel + { + Message = MESSAGE + }; + User user = new User + { + Id = creatorId, + }; + + this._postRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(post); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(user); + this._mapperMock + .Setup(p => p.Map<ReadPostServiceModel>(It.IsAny<Post>())) + .Returns(readPostServiceModel); + + ReadPostServiceModel result = await this._postService.GetPostById(Guid.NewGuid()); + + Assert.AreEqual(MESSAGE, result.Message); + } + + [Test] + public void GetPostById_ThorwsException_WhenTheUserDoesNotExist() + { + const string EXCEPTION_MESSAGE = "The user does not exist!"; + Guid creatorId = Guid.NewGuid(); + User creator = new User { Id = creatorId }; + Post post = new Post + { + Message = MESSAGE, + Creator = creator + }; + + this._postRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(post); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._postService.GetPostById(Guid.NewGuid()), "GetPostById does not throw exception when the user does not exist"); + + // Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message); + } + + [Test] + public void GetPostById_ThrowsException_WhenCommentDoesNotExist() + { + string exceptionMessage = "The post does not exist!"; + Guid creatorId = Guid.NewGuid(); + User user = new User + { + Id = creatorId, + }; + + this._postRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .Returns(Task.FromResult<Post>(null)); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(user); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._postService.GetPostById(Guid.NewGuid())); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region UpdatePost + [Test] + public async Task UpdatePost_ReturnsTheIdOfThePost_WhenUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + Post post = new Post + { + Id = id, + Message = MESSAGE + }; + UpdatePostServiceModel updatePostServiceModel = new UpdatePostServiceModel + { + PostId = id, + NewMessage = MESSAGE, + Files = new List<IFormFile>() + }; + + this._postRepositoryMock + .Setup(p => p.DoesPostExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._postRepositoryMock + .Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Post>())) + .ReturnsAsync(true); + this._postRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(post); + this._mapperMock + .Setup(p => p.Map<Post>(It.IsAny<UpdatePostServiceModel>())) + .Returns(post); + + Guid result = await this._postService.UpdatePost(updatePostServiceModel); + + Assert.AreEqual(updatePostServiceModel.PostId, result); + } + + [Test] + public async Task UpdatePost_ReturnsEmptyId_WhenThePostIsNotUpdatedSuccessfully() + { + Post post = new Post + { + Message = MESSAGE + }; + UpdatePostServiceModel updatePostServiceModel = new UpdatePostServiceModel + { + PostId = Guid.NewGuid(), + NewMessage = MESSAGE, + Files = new List<IFormFile>() + }; + + this._postRepositoryMock + .Setup(p => p.DoesPostExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._postRepositoryMock + .Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Post>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<Post>(It.IsAny<UpdatePostServiceModel>())) + .Returns(post); + + Guid result = await this._postService.UpdatePost(updatePostServiceModel); + + Assert.AreEqual(Guid.Empty, result); + } + + [Test] + public void UpdatePost_ThrowsArgumentException_WhenCommentDoesNotExist() + { + string exceptionMessage = "Post does not exist!"; + UpdatePostServiceModel updatePostServiceModel = new UpdatePostServiceModel + { + }; + + this._postRepositoryMock + .Setup(p => p.DoesPostExist(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._postService.UpdatePost(updatePostServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region DeletePost + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task Deletepost_ShouldReturnIfDeletionIsSuccessfull_WhenPostExists(bool shouldPass) + { + Guid id = Guid.NewGuid(); + Post post = new Post(); + + this._postRepositoryMock + .Setup(p => p.DoesPostExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._postRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(post); + this._postRepositoryMock + .Setup(p => p.DeleteAsync(It.IsAny<Post>())) + .ReturnsAsync(shouldPass); + + bool result = await this._postService.DeletePost(id); + + Assert.AreEqual(shouldPass, result); + } + + [Test] + public void DeletePost_ThrowsException_WhenPostDoesNotExist() + { + string exceptionMessage = "Post does not exist!"; + Guid id = Guid.NewGuid(); + + this._postRepositoryMock + .Setup(p => p.DoesPostExist(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._postService.DeletePost(id)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + } +} diff --git a/src/Services/DevHive.Services.Tests/ProfilePictureService.Tests.cs b/src/Services/DevHive.Services.Tests/ProfilePictureService.Tests.cs new file mode 100644 index 0000000..786de7b --- /dev/null +++ b/src/Services/DevHive.Services.Tests/ProfilePictureService.Tests.cs @@ -0,0 +1,84 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using DevHive.Data; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Data.Repositories; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.ProfilePicture; +using DevHive.Services.Services; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Moq; +using NUnit.Framework; + +namespace DevHive.Services.Tests +{ + [TestFixture] + public class ProfilePictureServiceTests + { + private DevHiveContext _context; + private Mock<IUserRepository> _userRepositoryMock; + private Mock<ProfilePictureRepository> _profilePictureRepository; + private Mock<ICloudService> _cloudService; + private ProfilePictureService _profilePictureService; + + [SetUp] + public void Setup() + { + DbContextOptionsBuilder<DevHiveContext> options = new DbContextOptionsBuilder<DevHiveContext>() + .UseInMemoryDatabase("DevHive_UserRepository_Database"); + this._context = new DevHiveContext(options.Options); + this._userRepositoryMock = new Mock<IUserRepository>(); + this._profilePictureRepository = new Mock<ProfilePictureRepository>(this._context); + this._cloudService = new Mock<ICloudService>(); + + this._profilePictureService = new ProfilePictureService( + this._userRepositoryMock.Object, + this._profilePictureRepository.Object, + this._cloudService.Object + ); + } + + // [Test] + // public async Task InsertProfilePicture_ShouldInsertProfilePicToDatabaseAndUploadToCloud() + // { + // //Arrange + // Guid userId = Guid.NewGuid(); + // Mock<IFormFile> fileMock = new(); + // + // //File mocking setup + // var content = "Hello World from a Fake File"; + // var fileName = "test.jpg"; + // var ms = new MemoryStream(); + // var writer = new StreamWriter(ms); + // writer.Write(content); + // writer.Flush(); + // ms.Position = 0; + // fileMock.Setup(p => p.FileName).Returns(fileName); + // fileMock.Setup(p => p.Length).Returns(ms.Length); + // fileMock.Setup(p => p.OpenReadStream()).Returns(ms); + // + // //User Setup + // this._userRepositoryMock + // .Setup(p => p.GetByIdAsync(userId)) + // .ReturnsAsync(new User() + // { + // Id = userId + // }); + // + // ProfilePictureServiceModel profilePictureServiceModel = new() + // { + // UserId = userId, + // ProfilePictureFormFile = fileMock.Object + // }; + // + // //Act + // string profilePicURL = await this._profilePictureService.UpdateProfilePicture(profilePictureServiceModel); + // + // //Assert + // Assert.IsNotEmpty(profilePicURL); + // } + } +} diff --git a/src/Services/DevHive.Services.Tests/RatingService.Tests.cs b/src/Services/DevHive.Services.Tests/RatingService.Tests.cs new file mode 100644 index 0000000..9f4300b --- /dev/null +++ b/src/Services/DevHive.Services.Tests/RatingService.Tests.cs @@ -0,0 +1,448 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Models.Rating; +using DevHive.Services.Services; +using Moq; +using NUnit.Framework; + +namespace DevHive.Services.Tests +{ + [TestFixture] + public class RatingServiceTests + { + private Mock<IPostRepository> _postRepositoryMock; + private Mock<IRatingRepository> _ratingRepositoryMock; + private Mock<IUserRepository> _userRepositoryMock; + private Mock<IMapper> _mapperMock; + private RatingService _ratingService; + + #region SetUps + [SetUp] + public void SetUp() + { + this._postRepositoryMock = new Mock<IPostRepository>(); + this._ratingRepositoryMock = new Mock<IRatingRepository>(); + this._userRepositoryMock = new Mock<IUserRepository>(); + this._mapperMock = new Mock<IMapper>(); + this._ratingService = new RatingService(this._postRepositoryMock.Object, this._ratingRepositoryMock.Object, this._userRepositoryMock.Object, this._mapperMock.Object); + } + #endregion + + #region Create + [Test] + public async Task RatePost_ReturnsNonEmptyGuid_WhenEntityIsAddedSuccessfully() + { + bool isLike = true; + Guid id = Guid.NewGuid(); + Guid postId = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + CreateRatingServiceModel createRatingServiceModel = new CreateRatingServiceModel + { + PostId = postId, + UserId = userId, + IsLike = isLike + }; + Rating rating = new Rating + { + Id = id, + IsLike = isLike + }; + User user = new User + { + Id = userId + }; + Post post = new Post + { + Id = postId + }; + + this._postRepositoryMock + .Setup(p => p.DoesPostExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._ratingRepositoryMock + .Setup(p => p.UserRatedPost(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(false); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(user); + this._postRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(post); + this._ratingRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Rating>())) + .ReturnsAsync(true); + this._ratingRepositoryMock + .Setup(p => p.GetRatingByUserAndPostId(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(rating); + this._mapperMock + .Setup(p => p.Map<Rating>(It.IsAny<CreateRatingServiceModel>())) + .Returns(rating); + + Guid result = await this._ratingService.RatePost(createRatingServiceModel); + + Assert.AreEqual(id, result); + } + + [Test] + public async Task RatePost_ReturnsEmptyGuid_WhenEntityIsNotAddedSuccessfully() + { + bool isLike = true; + Guid id = Guid.NewGuid(); + Guid postId = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + CreateRatingServiceModel createRatingServiceModel = new CreateRatingServiceModel + { + PostId = postId, + UserId = userId, + IsLike = isLike + }; + Rating rating = new Rating + { + Id = id, + IsLike = isLike + }; + User user = new User + { + Id = userId + }; + Post post = new Post + { + Id = postId + }; + + this._postRepositoryMock + .Setup(p => p.DoesPostExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._ratingRepositoryMock + .Setup(p => p.UserRatedPost(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(false); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(user); + this._postRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(post); + this._ratingRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Rating>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<Rating>(It.IsAny<CreateRatingServiceModel>())) + .Returns(rating); + + Guid result = await this._ratingService.RatePost(createRatingServiceModel); + + Assert.AreEqual(result, Guid.Empty); + } + #endregion + + #region Read + [Test] + public async Task GetRatingById_ReturnsTheRating_WhenItExists() + { + Guid id = Guid.NewGuid(); + bool isLike = true; + User user = new User + { + Id = Guid.NewGuid() + }; + Rating rating = new Rating + { + Id = id, + IsLike = isLike, + User = user + }; + ReadRatingServiceModel readRatingServiceModel = new ReadRatingServiceModel + { + Id = id, + IsLike = isLike + }; + + this._ratingRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(rating); + this._mapperMock + .Setup(p => p.Map<ReadRatingServiceModel>(It.IsAny<Rating>())) + .Returns(readRatingServiceModel); + + ReadRatingServiceModel result = await this._ratingService.GetRatingById(id); + + Assert.AreEqual(isLike, result.IsLike); + } + + // [Test] + // public void GetRatingById_ThrowsException_WhenRatingDoesNotExist() + // { + // string exceptionMessage = "The rating does not exist"; + // this._ratingRepositoryMock + // .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + // .Returns(Task.FromResult<Rating>(null)); + // + // Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this._ratingService.GetRatingById(Guid.Empty)); + // + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + // } + + [Test] + public async Task GetRatingByPostAndUser_ReturnsTheRating_WhenItExists() + { + Guid id = Guid.NewGuid(); + bool isLike = true; + User user = new User + { + Id = Guid.NewGuid() + }; + Rating rating = new Rating + { + Id = id, + IsLike = isLike, + User = user + }; + ReadRatingServiceModel readRatingServiceModel = new ReadRatingServiceModel + { + Id = id, + IsLike = isLike + }; + + this._ratingRepositoryMock + .Setup(p => p.GetRatingByUserAndPostId(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(rating); + this._mapperMock + .Setup(p => p.Map<ReadRatingServiceModel>(It.IsAny<Rating>())) + .Returns(readRatingServiceModel); + + ReadRatingServiceModel result = await this._ratingService.GetRatingByPostAndUser(user.Id, Guid.Empty); + + Assert.AreEqual(isLike, result.IsLike); + } + + // [Test] + // public void GetRatingByPostAndUser_ThrowsException_WhenRatingDoesNotExist() + // { + // string exceptionMessage = "The rating does not exist"; + // this._ratingRepositoryMock + // .Setup(p => p.GetRatingByUserAndPostId(It.IsAny<Guid>(), It.IsAny<Guid>())) + // .Returns(Task.FromResult<Rating>(null)); + // + // Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this._ratingService.GetRatingById(Guid.Empty)); + // + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + // } + #endregion + + #region Update + [Test] + public async Task UpdateRating_ReturnsObject_WhenRatingIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + bool isLike = true; + UpdateRatingServiceModel updateRatingServiceModel = new UpdateRatingServiceModel + { + Id = id, + IsLike = isLike + }; + User user = new User + { + Id = Guid.NewGuid() + }; + Rating rating = new Rating + { + Id = id, + IsLike = isLike, + User = user + }; + ReadRatingServiceModel readRatingServiceModel = new ReadRatingServiceModel + { + Id = id, + IsLike = isLike + }; + + this._ratingRepositoryMock + .Setup(p => p.GetRatingByUserAndPostId(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(rating); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(user); + this._ratingRepositoryMock + .Setup(p => p.UserRatedPost(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(true); + this._ratingRepositoryMock + .Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Rating>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<ReadRatingServiceModel>(It.IsAny<Rating>())) + .Returns(readRatingServiceModel); + + ReadRatingServiceModel result = await this._ratingService.UpdateRating(updateRatingServiceModel); + + Assert.AreEqual(result, readRatingServiceModel); + } + + [Test] + public async Task UpdateRating_ReturnsNull_WhenRatingIsNotUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + bool isLike = true; + UpdateRatingServiceModel updateRatingServiceModel = new UpdateRatingServiceModel + { + Id = id, + IsLike = isLike + }; + User user = new User + { + Id = Guid.NewGuid() + }; + Rating rating = new Rating + { + Id = id, + IsLike = isLike, + User = user + }; + ReadRatingServiceModel readRatingServiceModel = new ReadRatingServiceModel + { + Id = id, + IsLike = isLike + }; + + this._ratingRepositoryMock + .Setup(p => p.GetRatingByUserAndPostId(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(rating); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(user); + this._ratingRepositoryMock + .Setup(p => p.UserRatedPost(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(true); + this._ratingRepositoryMock + .Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Rating>())) + .ReturnsAsync(false); + + ReadRatingServiceModel result = await this._ratingService.UpdateRating(updateRatingServiceModel); + + Assert.IsNull(result); + } + + [Test] + public void UpdateRating_ThrowsException_WhenRatingDoesNotExists() + { + string exceptionMessage = "Rating does not exist!"; + UpdateRatingServiceModel updateRatingServiceModel = new UpdateRatingServiceModel + { + Id = Guid.Empty, + IsLike = true + }; + + this._ratingRepositoryMock + .Setup(p => p.GetRatingByUserAndPostId(It.IsAny<Guid>(), It.IsAny<Guid>())) + .Returns(Task.FromResult<Rating>(null)); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._ratingService.UpdateRating(updateRatingServiceModel)); + + // Assert.AreEqual(ex.Message, exceptionMessage); + } + + [Test] + public void UpdateRating_ThrowsException_WhenUserHasNotRatedPost() + { + string exceptionMessage = "User has not rated the post!"; + Guid id = Guid.NewGuid(); + bool isLike = true; + UpdateRatingServiceModel updateRatingServiceModel = new UpdateRatingServiceModel + { + Id = id, + IsLike = isLike + }; + User user = new User + { + Id = Guid.NewGuid() + }; + Rating rating = new Rating + { + Id = id, + IsLike = isLike, + User = user + }; + + this._ratingRepositoryMock + .Setup(p => p.GetRatingByUserAndPostId(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(rating); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(user); + this._ratingRepositoryMock + .Setup(p => p.UserRatedPost(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentException>(() => this._ratingService.UpdateRating(updateRatingServiceModel)); + + // Assert.AreEqual(ex.Message, exceptionMessage); + } + #endregion + + #region Delete + [Test] + public async Task DeleteRating_ReturnsTrue_WhenRatingIsDeletedSuccessfully() + { + Guid ratingId = Guid.NewGuid(); + Rating rating = new Rating + { + Id = ratingId + }; + + this._ratingRepositoryMock + .Setup(p => p.DoesRatingExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._ratingRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .Returns(Task.FromResult<Rating>(null)); + this._ratingRepositoryMock + .Setup(p => p.DeleteAsync(It.IsAny<Rating>())) + .ReturnsAsync(true); + + bool result = await this._ratingService.DeleteRating(ratingId); + + Assert.IsTrue(result); + } + + [Test] + public async Task DeleteRating_ReturnsFalse_WhenRatingIsNotDeletedSuccessfully() + { + Guid ratingId = Guid.NewGuid(); + Rating rating = new Rating + { + Id = ratingId + }; + + this._ratingRepositoryMock + .Setup(p => p.DoesRatingExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._ratingRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .Returns(Task.FromResult<Rating>(null)); + this._ratingRepositoryMock + .Setup(p => p.DeleteAsync(It.IsAny<Rating>())) + .ReturnsAsync(false); + + bool result = await this._ratingService.DeleteRating(ratingId); + + Assert.IsFalse(result); + } + + [Test] + public void DeleteRating_ThrowsException_WhenRatingDoesNotExist() + { + string exceptionMessage = "Rating does not exist!"; + + this._ratingRepositoryMock + .Setup(p => p.DoesRatingExist(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._ratingService.DeleteRating(Guid.Empty)); + + // Assert.AreEqual(ex.Message, exceptionMessage); + } + #endregion + } +} diff --git a/src/Services/DevHive.Services.Tests/RoleService.Tests.cs b/src/Services/DevHive.Services.Tests/RoleService.Tests.cs new file mode 100644 index 0000000..c286c80 --- /dev/null +++ b/src/Services/DevHive.Services.Tests/RoleService.Tests.cs @@ -0,0 +1,271 @@ +using System; +using System.Data; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Models.Role; +using DevHive.Services.Services; +using Moq; +using NUnit.Framework; + +namespace DevHive.Services.Tests +{ + [TestFixture] + public class RoleServiceTests + { + private Mock<IRoleRepository> _roleRepositoryMock; + private Mock<IMapper> _mapperMock; + private RoleService _roleService; + + #region SetUps + [SetUp] + public void Setup() + { + this._roleRepositoryMock = new Mock<IRoleRepository>(); + this._mapperMock = new Mock<IMapper>(); + this._roleService = new RoleService(this._roleRepositoryMock.Object, this._mapperMock.Object); + } + #endregion + + #region CreateRole + [Test] + public async Task CreateRole_ReturnsNonEmptyGuid_WhenEntityIsAddedSuccessfully() + { + string roleName = "Gosho Trapov"; + Guid id = Guid.NewGuid(); + CreateRoleServiceModel createRoleServiceModel = new CreateRoleServiceModel + { + Name = roleName + }; + Role role = new() + { + Name = roleName, + Id = id + }; + + this._roleRepositoryMock + .Setup(p => p.DoesNameExist(It.IsAny<string>())) + .ReturnsAsync(false); + this._roleRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Role>())) + .ReturnsAsync(true); + this._roleRepositoryMock + .Setup(p => p.GetByNameAsync(It.IsAny<string>())) + .ReturnsAsync(role); + this._mapperMock + .Setup(p => p.Map<Role>(It.IsAny<CreateRoleServiceModel>())) + .Returns(role); + + Guid result = await this._roleService.CreateRole(createRoleServiceModel); + + Assert.AreEqual(id, result); + } + + [Test] + public async Task CreateRoley_ReturnsEmptyGuid_WhenEntityIsNotAddedSuccessfully() + { + string roleName = "Gosho Trapov"; + + CreateRoleServiceModel createRoleServiceModel = new CreateRoleServiceModel + { + Name = roleName + }; + Role role = new Role + { + Name = roleName + }; + + this._roleRepositoryMock + .Setup(p => p.DoesNameExist(It.IsAny<string>())) + .ReturnsAsync(false); + this._roleRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Role>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<Role>(It.IsAny<CreateRoleServiceModel>())) + .Returns(role); + + Guid result = await this._roleService.CreateRole(createRoleServiceModel); + + Assert.IsTrue(result == Guid.Empty); + } + + [Test] + public void CreateTechnology_ThrowsArgumentException_WhenEntityAlreadyExists() + { + string exceptionMessage = "Role already exists!"; + string roleName = "Gosho Trapov"; + + CreateRoleServiceModel createRoleServiceModel = new CreateRoleServiceModel + { + Name = roleName + }; + + this._roleRepositoryMock + .Setup(p => p.DoesNameExist(It.IsAny<string>())) + .ReturnsAsync(true); + + Exception ex = Assert.ThrowsAsync<DuplicateNameException>(() => this._roleService.CreateRole(createRoleServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region GetRoleById + [Test] + public async Task GetRoleById_ReturnsTheRole_WhenItExists() + { + Guid id = Guid.NewGuid(); + string name = "Gosho Trapov"; + Role role = new Role + { + Name = name + }; + RoleServiceModel roleServiceModel = new RoleServiceModel + { + Name = name + }; + + this._roleRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(role); + this._mapperMock + .Setup(p => p.Map<RoleServiceModel>(It.IsAny<Role>())) + .Returns(roleServiceModel); + + RoleServiceModel result = await this._roleService.GetRoleById(id); + + Assert.AreEqual(name, result.Name); + } + + [Test] + public void GetRoleById_ThrowsException_WhenRoleDoesNotExist() + { + string exceptionMessage = "Role does not exist!"; + Guid id = Guid.NewGuid(); + this._roleRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .Returns(Task.FromResult<Role>(null)); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._roleService.GetRoleById(id)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region UpdateRole + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task UpdateRole_ReturnsIfUpdateIsSuccessfull_WhenRoleExistsy(bool shouldPass) + { + string name = "Gosho Trapov"; + Guid id = Guid.NewGuid(); + Role role = new Role + { + Name = name, + Id = id + }; + UpdateRoleServiceModel updateRoleServiceModel = new UpdateRoleServiceModel + { + Name = name, + }; + + this._roleRepositoryMock + .Setup(p => p.DoesRoleExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._roleRepositoryMock + .Setup(p => p.DoesNameExist(It.IsAny<string>())) + .ReturnsAsync(false); + this._roleRepositoryMock + .Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Role>())) + .ReturnsAsync(shouldPass); + this._mapperMock + .Setup(p => p.Map<Role>(It.IsAny<UpdateRoleServiceModel>())) + .Returns(role); + + bool result = await this._roleService.UpdateRole(updateRoleServiceModel); + + Assert.AreEqual(shouldPass, result); + } + + [Test] + public void UpdateRole_ThrowsException_WhenRoleDoesNotExist() + { + string exceptionMessage = "Role does not exist!"; + UpdateRoleServiceModel updateRoleServiceModel = new UpdateRoleServiceModel + { + }; + + this._roleRepositoryMock + .Setup(p => p.DoesRoleExist(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._roleService.UpdateRole(updateRoleServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + + [Test] + public void UpdateRole_ThrowsException_WhenRoleNameAlreadyExists() + { + string exceptionMessage = "Role name already exists!"; + UpdateRoleServiceModel updateRoleServiceModel = new UpdateRoleServiceModel + { + }; + + this._roleRepositoryMock + .Setup(p => p.DoesRoleExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._roleRepositoryMock + .Setup(p => p.DoesNameExist(It.IsAny<string>())) + .ReturnsAsync(true); + + Exception ex = Assert.ThrowsAsync<DuplicateNameException>(() => this._roleService.UpdateRole(updateRoleServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region DeleteRole + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task DeleteRole_ShouldReturnIfDeletionIsSuccessfull_WhenRoleExists(bool shouldPass) + { + Guid id = Guid.NewGuid(); + Role role = new Role(); + + this._roleRepositoryMock + .Setup(p => p.DoesRoleExist(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._roleRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(role); + this._roleRepositoryMock + .Setup(p => p.DeleteAsync(It.IsAny<Role>())) + .ReturnsAsync(shouldPass); + + bool result = await this._roleService.DeleteRole(id); + + Assert.AreEqual(shouldPass, result); + } + + [Test] + public void DeleteRole_ThrowsException_WhenRoleDoesNotExist() + { + string exceptionMessage = "Role does not exist!"; + Guid id = Guid.NewGuid(); + + this._roleRepositoryMock + .Setup(p => p.DoesRoleExist(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._roleService.DeleteRole(id)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + } +} diff --git a/src/Services/DevHive.Services.Tests/TechnologyServices.Tests.cs b/src/Services/DevHive.Services.Tests/TechnologyServices.Tests.cs new file mode 100644 index 0000000..e11650f --- /dev/null +++ b/src/Services/DevHive.Services.Tests/TechnologyServices.Tests.cs @@ -0,0 +1,311 @@ +using AutoMapper; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Models.Technology; +using DevHive.Services.Services; +using Moq; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Data; +using System.Threading.Tasks; + +namespace DevHive.Services.Tests +{ + [TestFixture] + public class TechnologyServicesTests + { + private Mock<ITechnologyRepository> _technologyRepositoryMock; + private Mock<IMapper> _mapperMock; + private TechnologyService _technologyService; + + #region SetUps + [SetUp] + public void Setup() + { + this._technologyRepositoryMock = new Mock<ITechnologyRepository>(); + this._mapperMock = new Mock<IMapper>(); + this._technologyService = new TechnologyService(this._technologyRepositoryMock.Object, this._mapperMock.Object); + } + #endregion + + #region CreateTechnology + [Test] + public async Task CreateTechnology_ReturnsNonEmptyGuid_WhenEntityIsAddedSuccessfully() + { + string technologyName = "Gosho Trapov"; + Guid id = Guid.NewGuid(); + CreateTechnologyServiceModel createTechnologyServiceModel = new() + { + Name = technologyName + }; + Technology technology = new() + { + Name = technologyName, + Id = id + }; + + this._technologyRepositoryMock + .Setup(p => p.DoesTechnologyNameExistAsync(It.IsAny<string>())) + .ReturnsAsync(false); + this._technologyRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Technology>())) + .ReturnsAsync(true); + this._technologyRepositoryMock + .Setup(p => p.GetByNameAsync(It.IsAny<string>())) + .ReturnsAsync(technology); + this._mapperMock + .Setup(p => p.Map<Technology>(It.IsAny<CreateTechnologyServiceModel>())) + .Returns(technology); + + Guid result = await this._technologyService.CreateTechnology(createTechnologyServiceModel); + + Assert.AreEqual(id, result); + } + + [Test] + public async Task CreateTechnology_ReturnsEmptyGuid_WhenEntityIsNotAddedSuccessfully() + { + string technologyName = "Gosho Trapov"; + + CreateTechnologyServiceModel createTechnologyServiceModel = new() + { + Name = technologyName + }; + Technology technology = new() + { + Name = technologyName + }; + + this._technologyRepositoryMock + .Setup(p => p.DoesTechnologyNameExistAsync(It.IsAny<string>())) + .ReturnsAsync(false); + this._technologyRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<Technology>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<Technology>(It.IsAny<CreateTechnologyServiceModel>())) + .Returns(technology); + + Guid result = await this._technologyService.CreateTechnology(createTechnologyServiceModel); + + Assert.IsTrue(result == Guid.Empty); + } + + [Test] + public void CreateTechnology_ThrowsArgumentException_WhenEntityAlreadyExists() + { + string exceptionMessage = "Technology already exists!"; + string technologyName = "Gosho Trapov"; + + CreateTechnologyServiceModel createTechnologyServiceModel = new() + { + Name = technologyName + }; + + this._technologyRepositoryMock + .Setup(p => p.DoesTechnologyNameExistAsync(It.IsAny<string>())) + .ReturnsAsync(true); + + Exception ex = Assert.ThrowsAsync<DuplicateNameException>(() => this._technologyService.CreateTechnology(createTechnologyServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region GetTechnologyById + [Test] + public async Task GetTechnologyById_ReturnsTheTechnology_WhenItExists() + { + Guid id = Guid.NewGuid(); + string name = "Gosho Trapov"; + Technology technology = new() + { + Name = name + }; + ReadTechnologyServiceModel readTechnologyServiceModel = new() + { + Name = name + }; + + this._technologyRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(technology); + this._mapperMock + .Setup(p => p.Map<ReadTechnologyServiceModel>(It.IsAny<Technology>())) + .Returns(readTechnologyServiceModel); + + ReadTechnologyServiceModel result = await this._technologyService.GetTechnologyById(id); + + Assert.AreEqual(name, result.Name); + } + + [Test] + public void GetTechnologyById_ThrowsException_WhenTechnologyDoesNotExist() + { + string exceptionMessage = "The technology does not exist"; + Guid id = Guid.NewGuid(); + this._technologyRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .Returns(Task.FromResult<Technology>(null)); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._technologyService.GetTechnologyById(id)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region GetTechnologies + [Test] + public void GetTechnologies_ReturnsAllLanguages_IfAnyExist() + { + ReadTechnologyServiceModel firstTechnology = new ReadTechnologyServiceModel(); + ReadTechnologyServiceModel secondTechnology = new ReadTechnologyServiceModel(); + HashSet<ReadTechnologyServiceModel> technologies = new HashSet<ReadTechnologyServiceModel>(); + technologies.Add(firstTechnology); + technologies.Add(secondTechnology); + + this._technologyRepositoryMock + .Setup(p => p.GetTechnologies()) + .Returns(new HashSet<Technology>()); + this._mapperMock + .Setup(p => p.Map<HashSet<ReadTechnologyServiceModel>>(It.IsAny<HashSet<Technology>>())) + .Returns(technologies); + + HashSet<ReadTechnologyServiceModel> result = this._technologyService.GetTechnologies(); + + Assert.GreaterOrEqual(2, result.Count, "GetTechnologies does not return all technologies"); + } + + [Test] + public void GetLanguages_ReturnsEmptyHashSet_IfNoLanguagesExist() + { + this._technologyRepositoryMock + .Setup(p => p.GetTechnologies()) + .Returns(new HashSet<Technology>()); + this._mapperMock + .Setup(p => p.Map<HashSet<ReadTechnologyServiceModel>>(It.IsAny<HashSet<Technology>>())) + .Returns(new HashSet<ReadTechnologyServiceModel>()); + + HashSet<ReadTechnologyServiceModel> result = this._technologyService.GetTechnologies(); + + Assert.IsEmpty(result, "GetTechnologies does not return empty string when no technologies exist"); + } + #endregion + + #region UpdateTechnology + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task UpdateTechnology_ReturnsIfUpdateIsSuccessfull_WhenTechnologyExistsy(bool shouldPass) + { + string name = "Gosho Trapov"; + Guid id = Guid.NewGuid(); + Technology technology = new Technology + { + Name = name, + Id = id + }; + UpdateTechnologyServiceModel updatetechnologyServiceModel = new UpdateTechnologyServiceModel + { + Name = name, + }; + + this._technologyRepositoryMock + .Setup(p => p.DoesTechnologyExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._technologyRepositoryMock + .Setup(p => p.DoesTechnologyNameExistAsync(It.IsAny<string>())) + .ReturnsAsync(false); + this._technologyRepositoryMock + .Setup(p => p.EditAsync(It.IsAny<Guid>(), It.IsAny<Technology>())) + .ReturnsAsync(shouldPass); + this._mapperMock + .Setup(p => p.Map<Technology>(It.IsAny<UpdateTechnologyServiceModel>())) + .Returns(technology); + + bool result = await this._technologyService.UpdateTechnology(updatetechnologyServiceModel); + + Assert.AreEqual(shouldPass, result); + } + + [Test] + public void UpdateTechnology_ThrowsException_WhenTechnologyDoesNotExist() + { + string exceptionMessage = "Technology does not exist!"; + UpdateTechnologyServiceModel updateTechnologyServiceModel = new UpdateTechnologyServiceModel + { + }; + + this._technologyRepositoryMock + .Setup(p => p.DoesTechnologyExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._technologyService.UpdateTechnology(updateTechnologyServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + + [Test] + public void UpdateTechnology_ThrowsException_WhenTechnologyNameAlreadyExists() + { + string exceptionMessage = "Technology name already exists!"; + UpdateTechnologyServiceModel updateTechnologyServiceModel = new UpdateTechnologyServiceModel + { + }; + + this._technologyRepositoryMock + .Setup(p => p.DoesTechnologyExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._technologyRepositoryMock + .Setup(p => p.DoesTechnologyNameExistAsync(It.IsAny<string>())) + .ReturnsAsync(true); + + Exception ex = Assert.ThrowsAsync<DuplicateNameException>(() => this._technologyService.UpdateTechnology(updateTechnologyServiceModel)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + + #region DeleteTechnology + + [Test] + [TestCase(true)] + [TestCase(false)] + public async Task DeleteTechnology_ShouldReturnIfDeletionIsSuccessfull_WhenTechnologyExists(bool shouldPass) + { + Guid id = Guid.NewGuid(); + Technology technology = new Technology(); + + this._technologyRepositoryMock + .Setup(p => p.DoesTechnologyExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._technologyRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(technology); + this._technologyRepositoryMock + .Setup(p => p.DeleteAsync(It.IsAny<Technology>())) + .ReturnsAsync(shouldPass); + + bool result = await this._technologyService.DeleteTechnology(id); + + Assert.AreEqual(shouldPass, result); + } + + [Test] + public void DeleteTechnology_ThrowsException_WhenTechnologyDoesNotExist() + { + string exceptionMessage = "Technology does not exist!"; + Guid id = Guid.NewGuid(); + + this._technologyRepositoryMock + .Setup(p => p.DoesTechnologyExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._technologyService.DeleteTechnology(id)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorecct exception message"); + } + #endregion + } +} diff --git a/src/Services/DevHive.Services.Tests/UserService.Tests.cs b/src/Services/DevHive.Services.Tests/UserService.Tests.cs new file mode 100644 index 0000000..44ca7b4 --- /dev/null +++ b/src/Services/DevHive.Services.Tests/UserService.Tests.cs @@ -0,0 +1,429 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Common.Models.Identity; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.User; +using DevHive.Services.Services; +using Moq; +using NUnit.Framework; + +namespace DevHive.Services.Tests +{ + [TestFixture] + public class UserServiceTests + { + private Mock<ICloudService> _cloudServiceMock; + private Mock<IUserRepository> _userRepositoryMock; + private Mock<IRoleRepository> _roleRepositoryMock; + private Mock<ILanguageRepository> _languageRepositoryMock; + private Mock<ITechnologyRepository> _technologyRepositoryMock; + private Mock<IMapper> _mapperMock; + private Mock<IJwtService> _jwtServiceMock; + private UserService _userService; + + #region SetUps + [SetUp] + public void Setup() + { + this._userRepositoryMock = new Mock<IUserRepository>(); + this._roleRepositoryMock = new Mock<IRoleRepository>(); + this._cloudServiceMock = new Mock<ICloudService>(); + this._languageRepositoryMock = new Mock<ILanguageRepository>(); + this._technologyRepositoryMock = new Mock<ITechnologyRepository>(); + this._jwtServiceMock = new Mock<IJwtService>(); + this._mapperMock = new Mock<IMapper>(); + this._userService = new UserService( + this._userRepositoryMock.Object, + this._languageRepositoryMock.Object, + this._roleRepositoryMock.Object, + this._technologyRepositoryMock.Object, + this._mapperMock.Object, + this._jwtServiceMock.Object); + } + #endregion + + #region LoginUser + [Test] + public async Task LoginUser_ReturnsTokenModel_WhenLoggingUserIn() + { + string somePassword = "I'm_Nigga"; + + LoginServiceModel loginServiceModel = new() + { + Password = somePassword + }; + User user = new() + { + Id = Guid.NewGuid(), + PasswordHash = somePassword, + UserName = "g_trapov" + }; + + this._userRepositoryMock + .Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())) + .ReturnsAsync(true); + this._userRepositoryMock + .Setup(p => p.VerifyPassword(It.IsAny<User>(), It.IsAny<string>())) + .ReturnsAsync(true); + this._userRepositoryMock + .Setup(p => p.GetByUsernameAsync(It.IsAny<string>())) + .ReturnsAsync(user); + + string jwtSecurityToken = "akjdhfakndvlahdfkljahdlfkjhasldf"; + this._jwtServiceMock + .Setup(p => p.GenerateJwtToken(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<List<string>>())) + .Returns(jwtSecurityToken); + TokenModel tokenModel = await this._userService.LoginUser(loginServiceModel); + + Assert.AreEqual(jwtSecurityToken, tokenModel.Token, "LoginUser does not return the correct token"); + } + + [Test] + public void LoginUser_ThrowsException_WhenUserNameDoesNotExist() + { + LoginServiceModel loginServiceModel = new(); + + this._userRepositoryMock + .Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<InvalidDataException>( + () => this._userService.LoginUser(loginServiceModel)); + + Assert.AreEqual("Invalid The Username!", ex.Message, "Incorrect Exception message"); + } + + [Test] + public void LoginUser_ThrowsException_WhenPasswordIsIncorrect() + { + string somePassword = "I'm_Nigga"; + + LoginServiceModel loginServiceModel = new() + { + Password = somePassword + }; + User user = new() + { + Id = Guid.NewGuid(), + }; + + this._userRepositoryMock + .Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())) + .ReturnsAsync(true); + this._userRepositoryMock + .Setup(p => p.GetByUsernameAsync(It.IsAny<string>())) + .ReturnsAsync(user); + + Exception ex = Assert.ThrowsAsync<InvalidDataException>(() => this._userService.LoginUser(loginServiceModel)); + + Assert.AreEqual("Incorrect password!", ex.Message, "Incorrect Exception message"); + } + #endregion + + #region RegisterUser + [Test] + public async Task RegisterUser_ReturnsTokenModel_WhenUserIsSuccessfull() + { + Guid userId = Guid.NewGuid(); + RegisterServiceModel registerServiceModel = new() + { + Password = "ImNigga" + }; + User user = new() + { + Id = userId, + UserName = "g_trapov" + }; + Role role = new() { Name = Role.DefaultRole }; + + this._userRepositoryMock + .Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())) + .ReturnsAsync(false); + this._userRepositoryMock + .Setup(p => p.VerifyPassword(It.IsAny<User>(), It.IsAny<string>())) + .ReturnsAsync(true); + this._userRepositoryMock + .Setup(p => p.DoesEmailExistAsync(It.IsAny<string>())) + .ReturnsAsync(false); + this._userRepositoryMock + .Setup(p => p.AddAsync(It.IsAny<User>())) + .ReturnsAsync(true); + this._userRepositoryMock + .Setup(p => p.AddRoleToUser(It.IsAny<User>(), It.IsAny<string>())) + .ReturnsAsync(true); + this._userRepositoryMock + .Setup(p => p.GetByUsernameAsync(It.IsAny<string>())) + .ReturnsAsync(user); + + this._roleRepositoryMock + .Setup(p => p.DoesNameExist(It.IsAny<string>())) + .ReturnsAsync(true); + this._roleRepositoryMock + .Setup(p => p.GetByNameAsync(It.IsAny<string>())) + .ReturnsAsync(role); + + this._mapperMock + .Setup(p => p.Map<User>(It.IsAny<RegisterServiceModel>())) + .Returns(user); + + string jwtSecurityToken = "akjdhfakndvlahdfkljahdlfkjhasldf"; + this._jwtServiceMock + .Setup(p => p.GenerateJwtToken(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<List<string>>())) + .Returns(jwtSecurityToken); + TokenModel tokenModel = await this._userService.RegisterUser(registerServiceModel); + + Assert.AreEqual(jwtSecurityToken, tokenModel.Token, "RegisterUser does not return the correct token"); + } + + [Test] + public void RegisterUser_ThrowsException_WhenUsernameAlreadyExists() + { + const string EXCEPTION_MESSAGE = "The Username already exists!"; + RegisterServiceModel registerServiceModel = new(); + + this._userRepositoryMock + .Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())) + .ReturnsAsync(true); + + Exception ex = Assert.ThrowsAsync<DuplicateNameException>( + () => this._userService.RegisterUser(registerServiceModel)); + + Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorrect Exception message"); + } + + [Test] + public void RegisterUser_ThrowsException_WhenEmailAlreadyExists() + { + RegisterServiceModel registerServiceModel = new(); + + this._userRepositoryMock + .Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())) + .ReturnsAsync(false); + this._userRepositoryMock + .Setup(p => p.DoesEmailExistAsync(It.IsAny<string>())) + .ReturnsAsync(true); + + Exception ex = Assert.ThrowsAsync<DuplicateNameException>(() => this._userService.RegisterUser(registerServiceModel)); + + Assert.AreEqual("The Email already exists!", ex.Message, "Incorrect Exception message"); + } + #endregion + + #region GetUserById + [Test] + public async Task GetUserById_ReturnsTheUser_WhenItExists() + { + Guid id = new(); + string username = "g_trapov"; + User user = new(); + UserServiceModel userServiceModel = new() + { + UserName = username + }; + + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .ReturnsAsync(user); + this._mapperMock + .Setup(p => p.Map<UserServiceModel>(It.IsAny<User>())) + .Returns(userServiceModel); + + UserServiceModel result = await this._userService.GetUserById(id); + + Assert.AreEqual(username, result.UserName); + } + + [Test] + public void GetTechnologyById_ThrowsException_WhenTechnologyDoesNotExist() + { + Guid id = new(); + this._userRepositoryMock + .Setup(p => p.GetByIdAsync(It.IsAny<Guid>())) + .Returns(Task.FromResult<User>(null)); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._userService.GetUserById(id)); + + // Assert.AreEqual("User does not exist!", ex.Message, "Incorrect exception message"); + } + #endregion + + #region GetUserByUsername + [Test] + public async Task GetUserByUsername_ReturnsTheCorrectUser_IfItExists() + { + string username = "g_trapov"; + User user = new(); + UserServiceModel userServiceModel = new() + { + UserName = username + }; + + this._userRepositoryMock + .Setup(p => p.GetByUsernameAsync(It.IsAny<string>())) + .ReturnsAsync(user); + this._mapperMock + .Setup(p => p.Map<UserServiceModel>(It.IsAny<User>())) + .Returns(userServiceModel); + + UserServiceModel result = await this._userService.GetUserByUsername(username); + + Assert.AreEqual(username, result.UserName, "GetUserByUsername does not return the correct user"); + } + + [Test] + public void GetUserByUsername_ThrowsException_IfUserDoesNotExist() + { + string username = "g_trapov"; + + this._userRepositoryMock + .Setup(p => p.GetByUsernameAsync(It.IsAny<string>())) + .Returns(Task.FromResult<User>(null)); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._userService.GetUserByUsername(username)); + + // Assert.AreEqual("User does not exist!", ex.Message, "Incorrect exception message"); + } + #endregion + + #region UpdateUser + // [Test] + // [TestCase(true)] + // [TestCase(false)] + // public async Task UpdateUser_ReturnsIfUpdateIsSuccessfull_WhenUserExistsy(bool shouldPass) + // { + // string username = "g_trapov"; + // Guid id = Guid.NewGuid(); + // User user = new User + // { + // UserName = username, + // Id = id, + // }; + // UpdateUserServiceModel updateUserServiceModel = new UpdateUserServiceModel + // { + // UserName = username, + // }; + // UserServiceModel userServiceModel = new UserServiceModel + // { + // UserName = username, + // }; + // Role role = new Role { }; + // + // this._userRepositoryMock.Setup(p => + // p.DoesUserExistAsync(It.IsAny<Guid>())) + // .ReturnsAsync(true); + // this._userRepositoryMock.Setup(p => + // p.DoesUsernameExistAsync(It.IsAny<string>())) + // .ReturnsAsync(false); + // this._userRepositoryMock.Setup(p => + // p.DoesUserHaveThisUsernameAsync(It.IsAny<Guid>(), It.IsAny<string>())) + // .Returns(true); + // this._userRepositoryMock.Setup(p => + // p.EditAsync(It.IsAny<Guid>(), It.IsAny<User>())) + // .ReturnsAsync(shouldPass); + // this._userRepositoryMock.Setup(p => + // p.GetByIdAsync(It.IsAny<Guid>())) + // .ReturnsAsync(user); + // this._mapperMock.Setup(p => + // p.Map<User>(It.IsAny<UpdateUserServiceModel>())) + // .Returns(user); + // this._mapperMock.Setup(p => + // p.Map<UserServiceModel>(It.IsAny<User>())) + // .Returns(userServiceModel); + // + // if (shouldPass) + // { + // UserServiceModel result = await this._userService.UpdateUser(updateUserServiceModel); + // + // Assert.AreEqual(updateUserServiceModel.UserName, result.UserName); + // } + // else + // { + // const string EXCEPTION_MESSAGE = string.Format(ErrorMessages.CannotEdit, ClassesConstants.User.ToLower()); + // + // Exception ex = Assert.ThrowsAsync<InvalidOperationException>(() => this._userService.UpdateUser(updateUserServiceModel); + // + // Assert.AreEqual(EXCEPTION_MESSAGE, ex.Message, "Incorrect exception message"); + // } + // } + + [Test] + public void UpdateUser_ThrowsException_WhenUserDoesNotExist() + { + UpdateUserServiceModel updateUserServiceModel = new(); + + this._userRepositoryMock + .Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._userService.UpdateUser(updateUserServiceModel)); + + // Assert.AreEqual("User does not exist!", ex.Message, "Incorrect exception message"); + } + + [Test] + public void UpdateUser_ThrowsException_WhenUserNameAlreadyExists() + { + UpdateUserServiceModel updateUserServiceModel = new(); + + this._userRepositoryMock + .Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._userRepositoryMock + .Setup(p => p.DoesUsernameExistAsync(It.IsAny<string>())) + .ReturnsAsync(true); + + Exception ex = Assert.ThrowsAsync<DuplicateNameException>(() => this._userService.UpdateUser(updateUserServiceModel)); + + Assert.AreEqual("the username already exists!", ex.Message, "Incorrect exception message"); + } + #endregion + + #region DeleteUser + //TO DO: compleate once Viko has looked into the return type of UserService.DeleteUser + // [Test] + // [TestCase(true)] + // [TestCase(false)] + // public async Task DeleteUser_ShouldReturnIfDeletionIsSuccessfull_WhenUserExists(bool shouldPass) + // { + // Guid id = Guid.NewGuid(); + // User user = new User(); + // + // this._userRepositoryMock.Setup(p => + // p.DoesUserExistAsync(It.IsAny<Guid>())) + // .ReturnsAsync(true); + // this._userRepositoryMock.Setup(p => + // p.GetByIdAsync(It.IsAny<Guid>())) + // .ReturnsAsync(user); + // this._userRepositoryMock.Setup(p => + // p.DeleteAsync(It.IsAny<User>())) + // .ReturnsAsync(shouldPass); + // + // bool result = await this._userService.DeleteUser(id); + // + // Assert.AreEqual(shouldPass, result); + // } + // + [Test] + public void DeleteUser_ThrowsException_WhenUserDoesNotExist() + { + string exceptionMessage = "User does not exist!"; + Guid id = new(); + + this._userRepositoryMock + .Setup(p => p.DoesUserExistAsync(It.IsAny<Guid>())) + .ReturnsAsync(false); + + Exception ex = Assert.ThrowsAsync<ArgumentNullException>(() => this._userService.DeleteUser(id)); + + // Assert.AreEqual(exceptionMessage, ex.Message, "Incorrect exception message"); + } + #endregion + } +} diff --git a/src/DevHive.Services/Configurations/Mapping/CommentMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/CommentMappings.cs index 5ca2a9e..5ca2a9e 100644 --- a/src/DevHive.Services/Configurations/Mapping/CommentMappings.cs +++ b/src/Services/DevHive.Services/Configurations/Mapping/CommentMappings.cs diff --git a/src/DevHive.Services/Configurations/Mapping/FeedMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/FeedMappings.cs index 952e480..952e480 100644 --- a/src/DevHive.Services/Configurations/Mapping/FeedMappings.cs +++ b/src/Services/DevHive.Services/Configurations/Mapping/FeedMappings.cs diff --git a/src/DevHive.Services/Configurations/Mapping/LanguageMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/LanguageMappings.cs index 9c572df..9c572df 100644 --- a/src/DevHive.Services/Configurations/Mapping/LanguageMappings.cs +++ b/src/Services/DevHive.Services/Configurations/Mapping/LanguageMappings.cs diff --git a/src/DevHive.Services/Configurations/Mapping/PostMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/PostMappings.cs index 9362f90..1d7d88b 100644 --- a/src/DevHive.Services/Configurations/Mapping/PostMappings.cs +++ b/src/Services/DevHive.Services/Configurations/Mapping/PostMappings.cs @@ -12,7 +12,6 @@ namespace DevHive.Services.Configurations.Mapping public PostMappings() { CreateMap<CreatePostServiceModel, Post>(); - // .ForMember(dest => dest.Files, src => src.Ignore()); CreateMap<UpdatePostServiceModel, Post>() .ForMember(dest => dest.Id, src => src.MapFrom(p => p.PostId)) // .ForMember(dest => dest.Files, src => src.Ignore()) diff --git a/src/Services/DevHive.Services/Configurations/Mapping/RatingMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/RatingMappings.cs new file mode 100644 index 0000000..9ad5f25 --- /dev/null +++ b/src/Services/DevHive.Services/Configurations/Mapping/RatingMappings.cs @@ -0,0 +1,21 @@ +using AutoMapper; +using DevHive.Data.Models; +using DevHive.Services.Models.Rating; + +namespace DevHive.Services.Configurations.Mapping +{ + public class RatingMappings : Profile + { + public RatingMappings() + { + CreateMap<CreateRatingServiceModel, Rating>() + .ForMember(dest => dest.User, src => src.Ignore()) + .ForMember(dest => dest.Post, src => src.Ignore()) + .ForMember(dest => dest.Id, src => src.Ignore()); + + CreateMap<Rating, ReadRatingServiceModel>(); + + CreateMap<UpdateRatingServiceModel, Rating>(); + } + } +} diff --git a/src/DevHive.Services/Configurations/Mapping/RoleMapings.cs b/src/Services/DevHive.Services/Configurations/Mapping/RoleMapings.cs index e61a107..e870ab1 100644 --- a/src/DevHive.Services/Configurations/Mapping/RoleMapings.cs +++ b/src/Services/DevHive.Services/Configurations/Mapping/RoleMapings.cs @@ -1,6 +1,6 @@ -using DevHive.Data.Models; using AutoMapper; -using DevHive.Services.Models.Identity.Role; +using DevHive.Data.Models; +using DevHive.Services.Models.Role; namespace DevHive.Services.Configurations.Mapping { @@ -14,6 +14,8 @@ namespace DevHive.Services.Configurations.Mapping CreateMap<Role, RoleServiceModel>(); CreateMap<Role, UpdateRoleServiceModel>(); + + CreateMap<RoleServiceModel, UpdateRoleServiceModel>(); } } } diff --git a/src/DevHive.Services/Configurations/Mapping/TechnologyMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/TechnologyMappings.cs index 85b57f1..85b57f1 100644 --- a/src/DevHive.Services/Configurations/Mapping/TechnologyMappings.cs +++ b/src/Services/DevHive.Services/Configurations/Mapping/TechnologyMappings.cs diff --git a/src/DevHive.Services/Configurations/Mapping/UserMappings.cs b/src/Services/DevHive.Services/Configurations/Mapping/UserMappings.cs index 2b0f4ed..5a39f73 100644 --- a/src/DevHive.Services/Configurations/Mapping/UserMappings.cs +++ b/src/Services/DevHive.Services/Configurations/Mapping/UserMappings.cs @@ -1,8 +1,6 @@ using DevHive.Data.Models; using AutoMapper; -using DevHive.Services.Models.Identity.User; -using DevHive.Common.Models.Misc; -using DevHive.Data.RelationModels; +using DevHive.Services.Models.User; namespace DevHive.Services.Configurations.Mapping { @@ -11,21 +9,23 @@ namespace DevHive.Services.Configurations.Mapping public UserMappings() { CreateMap<UserServiceModel, User>(); - CreateMap<RegisterServiceModel, User>(); + CreateMap<RegisterServiceModel, User>() + .ForMember(dest => dest.PasswordHash, src => src.MapFrom(p => p.Password)); CreateMap<FriendServiceModel, User>() .ForMember(dest => dest.Friends, src => src.Ignore()); CreateMap<UpdateUserServiceModel, User>() - .ForMember(dest => dest.Friends, src => src.Ignore()) - .AfterMap((src, dest) => dest.PasswordHash = PasswordModifications.GeneratePasswordHash(src.Password)); + .ForMember(dest => dest.Friends, src => src.Ignore()); CreateMap<UpdateFriendServiceModel, User>(); CreateMap<User, UserServiceModel>() .ForMember(dest => dest.ProfilePictureURL, src => src.MapFrom(p => p.ProfilePicture.PictureURL)) .ForMember(dest => dest.Friends, src => src.MapFrom(p => p.Friends)); CreateMap<User, UpdateUserServiceModel>() - .ForMember(x => x.Password, opt => opt.Ignore()) .ForMember(dest => dest.ProfilePictureURL, src => src.MapFrom(p => p.ProfilePicture.PictureURL)); CreateMap<User, FriendServiceModel>(); + + CreateMap<UserServiceModel, UpdateUserServiceModel>(); + CreateMap<UserServiceModel, UpdateFriendServiceModel>(); } } } diff --git a/src/DevHive.Services/DevHive.Services.csproj b/src/Services/DevHive.Services/DevHive.Services.csproj index 66df209..2468711 100644 --- a/src/DevHive.Services/DevHive.Services.csproj +++ b/src/Services/DevHive.Services/DevHive.Services.csproj @@ -2,28 +2,26 @@ <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> - <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.1"> + <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0"/> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.4"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> - <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" /> - <ProjectReference Include="..\DevHive.Data\DevHive.Data.csproj" /> - - <PackageReference Include="AutoMapper" Version="10.1.1" /> - <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.0" /> - - <PackageReference Include="CloudinaryDotNet" Version="1.14.0"/> + <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.10.0"/> + <PackageReference Include="AutoMapper" Version="10.1.1"/> + <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1"/> + <PackageReference Include="CloudinaryDotNet" Version="1.15.1"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\DevHive.Common\DevHive.Common.csproj" /> + <ProjectReference Include="..\..\Data\DevHive.Data\DevHive.Data.csproj"/> + <ProjectReference Include="..\DevHive.Services.Models\DevHive.Services.Models.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common\DevHive.Common.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common.Models\DevHive.Common.Models.csproj"/> </ItemGroup> - <PropertyGroup> <EnableNETAnalyzers>true</EnableNETAnalyzers> <AnalysisLevel>latest</AnalysisLevel> </PropertyGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/src/DevHive.Services/Interfaces/ICloudService.cs b/src/Services/DevHive.Services/Interfaces/ICloudService.cs index 3ae7a24..040729f 100644 --- a/src/DevHive.Services/Interfaces/ICloudService.cs +++ b/src/Services/DevHive.Services/Interfaces/ICloudService.cs @@ -9,8 +9,6 @@ namespace DevHive.Services.Interfaces { Task<List<string>> UploadFilesToCloud(List<IFormFile> formFiles); - // Task<List<FileContentResult>> GetFilesFromCloud(List<string> fileUrls); - Task<bool> RemoveFilesFromCloud(List<string> fileUrls); } } diff --git a/src/DevHive.Services/Interfaces/ICommentService.cs b/src/Services/DevHive.Services/Interfaces/ICommentService.cs index e7409a8..6d92a5d 100644 --- a/src/DevHive.Services/Interfaces/ICommentService.cs +++ b/src/Services/DevHive.Services/Interfaces/ICommentService.cs @@ -6,7 +6,7 @@ namespace DevHive.Services.Interfaces { public interface ICommentService { - Task<Guid> AddComment(CreateCommentServiceModel createPostServiceModel); + Task<Guid> AddComment(CreateCommentServiceModel createCommentServiceModel); Task<ReadCommentServiceModel> GetCommentById(Guid id); diff --git a/src/DevHive.Services/Interfaces/IFeedService.cs b/src/Services/DevHive.Services/Interfaces/IFeedService.cs index b507b3b..b507b3b 100644 --- a/src/DevHive.Services/Interfaces/IFeedService.cs +++ b/src/Services/DevHive.Services/Interfaces/IFeedService.cs diff --git a/src/Services/DevHive.Services/Interfaces/IFriendsService.cs b/src/Services/DevHive.Services/Interfaces/IFriendsService.cs new file mode 100644 index 0000000..52f23f3 --- /dev/null +++ b/src/Services/DevHive.Services/Interfaces/IFriendsService.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; + +namespace DevHive.Services.Interfaces +{ + public interface IFriendsService + { + Task<bool> AddFriend(Guid userId, string friendUsername); + Task<bool> RemoveFriend(Guid userId, string friendUsername); + } +} diff --git a/src/DevHive.Services/Interfaces/ILanguageService.cs b/src/Services/DevHive.Services/Interfaces/ILanguageService.cs index fabbec2..fabbec2 100644 --- a/src/DevHive.Services/Interfaces/ILanguageService.cs +++ b/src/Services/DevHive.Services/Interfaces/ILanguageService.cs diff --git a/src/DevHive.Services/Interfaces/IPostService.cs b/src/Services/DevHive.Services/Interfaces/IPostService.cs index d35acfd..5ccecff 100644 --- a/src/DevHive.Services/Interfaces/IPostService.cs +++ b/src/Services/DevHive.Services/Interfaces/IPostService.cs @@ -4,7 +4,7 @@ using DevHive.Services.Models.Post; namespace DevHive.Services.Interfaces { - public interface IPostService + public interface IPostService { Task<Guid> CreatePost(CreatePostServiceModel createPostServiceModel); diff --git a/src/Services/DevHive.Services/Interfaces/IProfilePictureService.cs b/src/Services/DevHive.Services/Interfaces/IProfilePictureService.cs new file mode 100644 index 0000000..edf2775 --- /dev/null +++ b/src/Services/DevHive.Services/Interfaces/IProfilePictureService.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using DevHive.Services.Models.ProfilePicture; + +namespace DevHive.Services.Interfaces +{ + public interface IProfilePictureService + { + /// <summary> + /// Get a profile picture by it's Guid + /// </summary> + /// <param name="id">Profile picture's Guid</param> + /// <returns>The profile picture's URL in the cloud</returns> + Task<string> GetProfilePictureById(Guid id); + + /// <summary> + /// Uploads the given picture and assigns it's link to the user in the database + /// </summary> + /// <param name="profilePictureServiceModel">Contains User's Guid and the new picture to be updated</param> + /// <returns>The new profile picture's URL in the cloud</returns> + Task<string> UpdateProfilePicture(ProfilePictureServiceModel profilePictureServiceModel); + + /// <summary> + /// Delete a profile picture from the cloud and the database + /// </summary> + /// <param name="id">The profile picture's Guid</param> + /// <returns>True if the picture is deleted, false otherwise</returns> + Task<bool> DeleteProfilePicture(Guid id); + } +} diff --git a/src/Services/DevHive.Services/Interfaces/IRatingService.cs b/src/Services/DevHive.Services/Interfaces/IRatingService.cs new file mode 100644 index 0000000..be33300 --- /dev/null +++ b/src/Services/DevHive.Services/Interfaces/IRatingService.cs @@ -0,0 +1,20 @@ +using System; +using System.Threading.Tasks; +using DevHive.Data.Models; +using DevHive.Services.Models.Rating; + +namespace DevHive.Services.Interfaces +{ + public interface IRatingService + { + Task<Guid> RatePost(CreateRatingServiceModel createRatingServiceModel); + + Task<ReadRatingServiceModel> GetRatingById(Guid ratingId); + Task<ReadRatingServiceModel> GetRatingByPostAndUser(Guid userId, Guid postId); + + + Task<ReadRatingServiceModel> UpdateRating(UpdateRatingServiceModel updateRatingServiceModel); + + Task<bool> DeleteRating(Guid ratingId); + } +} diff --git a/src/DevHive.Services/Interfaces/IRoleService.cs b/src/Services/DevHive.Services/Interfaces/IRoleService.cs index d47728c..36e5a01 100644 --- a/src/DevHive.Services/Interfaces/IRoleService.cs +++ b/src/Services/DevHive.Services/Interfaces/IRoleService.cs @@ -1,16 +1,16 @@ using System; using System.Threading.Tasks; -using DevHive.Services.Models.Identity.Role; +using DevHive.Services.Models.Role; namespace DevHive.Services.Interfaces { public interface IRoleService { - Task<Guid> CreateRole(CreateRoleServiceModel roleServiceModel); + Task<Guid> CreateRole(CreateRoleServiceModel createRoleServiceModel); Task<RoleServiceModel> GetRoleById(Guid id); - Task<bool> UpdateRole(UpdateRoleServiceModel roleServiceModel); + Task<bool> UpdateRole(UpdateRoleServiceModel updateRoleServiceModel); Task<bool> DeleteRole(Guid id); } diff --git a/src/DevHive.Services/Interfaces/ITechnologyService.cs b/src/Services/DevHive.Services/Interfaces/ITechnologyService.cs index 8f9510c..8f9510c 100644 --- a/src/DevHive.Services/Interfaces/ITechnologyService.cs +++ b/src/Services/DevHive.Services/Interfaces/ITechnologyService.cs diff --git a/src/Services/DevHive.Services/Interfaces/IUserService.cs b/src/Services/DevHive.Services/Interfaces/IUserService.cs new file mode 100644 index 0000000..da07507 --- /dev/null +++ b/src/Services/DevHive.Services/Interfaces/IUserService.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading.Tasks; +using DevHive.Common.Models.Identity; +using DevHive.Services.Models.User; + +namespace DevHive.Services.Interfaces +{ + public interface IUserService + { + /// <summary> + /// Log ins an existing user and gives him/her a JWT Token for further authorization + /// </summary> + /// <param name="loginModel">Login service model, conaining user's username and password</param> + /// <returns>A JWT Token for authorization</returns> + Task<TokenModel> LoginUser(LoginServiceModel loginModel); + + /// <summary> + /// Registers a new user and gives him/her a JWT Token for further authorization + /// </summary> + /// <param name="registerModel">Register service model, containing the new user's data</param> + /// <returns>A JWT Token for authorization</returns> + Task<TokenModel> RegisterUser(RegisterServiceModel registerModel); + + /// <summary> + /// Get a user by his username. Used for querying profiles without provided authentication + /// </summary> + /// <param name="username">User's username, who's to be queried</param> + /// <returns>The queried user or null, if non existant</returns> + Task<UserServiceModel> GetUserByUsername(string username); + + /// <summary> + /// Get a user by his Guid. Used for querying full user's profile + /// Requires authenticated user + /// </summary> + /// <param name="id">User's username, who's to be queried</param> + /// <returns>The queried user or null, if non existant</returns> + Task<UserServiceModel> GetUserById(Guid id); + + /// <summary> + /// Updates a user's data, provided a full model with new details + /// Requires authenticated user + /// </summary> + /// <param name="updateUserServiceModel">Full update user model for updating</param> + /// <returns>Read model of the new user</returns> + Task<UserServiceModel> UpdateUser(UpdateUserServiceModel updateUserServiceModel); + + /// <summary> + /// Deletes a user from the database and removes his data entirely + /// Requires authenticated user + /// </summary> + /// <param name="id">The user's Guid, who's to be deleted</param> + /// <returns>True if successfull, false otherwise</returns> + Task<bool> DeleteUser(Guid id); + + /// <summary> + /// We don't talk about that! + /// </summary> + /// <param name="userId"></param> + /// <returns></returns> + Task<TokenModel> SuperSecretPromotionToAdmin(Guid userId); + } +} diff --git a/src/Services/DevHive.Services/Services/CloudinaryService.cs b/src/Services/DevHive.Services/Services/CloudinaryService.cs new file mode 100644 index 0000000..61d06fc --- /dev/null +++ b/src/Services/DevHive.Services/Services/CloudinaryService.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using CloudinaryDotNet; +using CloudinaryDotNet.Actions; +using DevHive.Services.Interfaces; +using Microsoft.AspNetCore.Http; + +namespace DevHive.Services.Services +{ + public class CloudinaryService : ICloudService + { + // Regex for getting the filename without (final) filename extension + // So, from image.png, it will match image, and from doc.my.txt will match doc.my + private static readonly Regex s_imageRegex = new(".*(?=\\.)"); + + private readonly Cloudinary _cloudinary; + + public CloudinaryService(string cloudName, string apiKey, string apiSecret) + { + this._cloudinary = new Cloudinary(new Account(cloudName, apiKey, apiSecret)); + } + + public async Task<List<string>> UploadFilesToCloud(List<IFormFile> formFiles) + { + List<string> fileUrls = new(); + foreach (var formFile in formFiles) + { + string fileName = s_imageRegex.Match(formFile.FileName).ToString(); + + using var ms = new MemoryStream(); + formFile.CopyTo(ms); + byte[] formBytes = ms.ToArray(); + + RawUploadParams rawUploadParams = new() + { + File = new FileDescription(fileName, new MemoryStream(formBytes)), + PublicId = fileName, + UseFilename = true + }; + + RawUploadResult rawUploadResult = await this._cloudinary.UploadAsync(rawUploadParams); + fileUrls.Add(rawUploadResult.Url.AbsoluteUri); + } + + return fileUrls; + } + + public async Task<bool> RemoveFilesFromCloud(List<string> fileUrls) + { + // Workaround, this method isn't fully implemented yet + await Task.Run(() => {}); + + return true; + } + } +} diff --git a/src/DevHive.Services/Services/CommentService.cs b/src/Services/DevHive.Services/Services/CommentService.cs index e2b54c4..1c388f6 100644 --- a/src/DevHive.Services/Services/CommentService.cs +++ b/src/Services/DevHive.Services/Services/CommentService.cs @@ -1,14 +1,15 @@ using System; using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; using AutoMapper; +using DevHive.Common.Constants; +using DevHive.Data.Interfaces; using DevHive.Data.Models; -using DevHive.Services.Models.Comment; -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; using DevHive.Services.Interfaces; -using DevHive.Data.Interfaces.Repositories; -using System.Linq; +using DevHive.Services.Models.Comment; namespace DevHive.Services.Services { @@ -31,7 +32,7 @@ namespace DevHive.Services.Services public async Task<Guid> AddComment(CreateCommentServiceModel createCommentServiceModel) { if (!await this._postRepository.DoesPostExist(createCommentServiceModel.PostId)) - throw new ArgumentException("Post does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Post)); Comment comment = this._postMapper.Map<Comment>(createCommentServiceModel); comment.TimeCreated = DateTime.Now; @@ -56,10 +57,10 @@ namespace DevHive.Services.Services public async Task<ReadCommentServiceModel> GetCommentById(Guid id) { Comment comment = await this._commentRepository.GetByIdAsync(id) ?? - throw new ArgumentException("The comment does not exist"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Comment)); User user = await this._userRepository.GetByIdAsync(comment.Creator.Id) ?? - throw new ArgumentException("The user does not exist"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); ReadCommentServiceModel readCommentServiceModel = this._postMapper.Map<ReadCommentServiceModel>(comment); readCommentServiceModel.IssuerFirstName = user.FirstName; @@ -74,7 +75,7 @@ namespace DevHive.Services.Services public async Task<Guid> UpdateComment(UpdateCommentServiceModel updateCommentServiceModel) { if (!await this._commentRepository.DoesCommentExist(updateCommentServiceModel.CommentId)) - throw new ArgumentException("Comment does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Comment)); Comment comment = this._postMapper.Map<Comment>(updateCommentServiceModel); comment.TimeCreated = DateTime.Now; @@ -95,7 +96,7 @@ namespace DevHive.Services.Services public async Task<bool> DeleteComment(Guid id) { if (!await this._commentRepository.DoesCommentExist(id)) - throw new ArgumentException("Comment does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Comment)); Comment comment = await this._commentRepository.GetByIdAsync(id); return await this._commentRepository.DeleteAsync(comment); @@ -121,7 +122,7 @@ namespace DevHive.Services.Services public async Task<bool> ValidateJwtForComment(Guid commentId, string rawTokenData) { Comment comment = await this._commentRepository.GetByIdAsync(commentId) ?? - throw new ArgumentException("Comment does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Comment)); User user = await this.GetUserForValidation(rawTokenData); //If user made the comment @@ -141,11 +142,10 @@ namespace DevHive.Services.Services { JwtSecurityToken jwt = new JwtSecurityTokenHandler().ReadJwtToken(rawTokenData.Remove(0, 7)); - Guid jwtUserId = Guid.Parse(this.GetClaimTypeValues("ID", jwt.Claims).First()); - //HashSet<string> jwtRoleNames = this.GetClaimTypeValues("role", jwt.Claims); + Guid jwtUserId = Guid.Parse(GetClaimTypeValues("ID", jwt.Claims).First()); User user = await this._userRepository.GetByIdAsync(jwtUserId) ?? - throw new ArgumentException("User does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); return user; } @@ -153,7 +153,7 @@ namespace DevHive.Services.Services /// <summary> /// Returns all values from a given claim type /// </summary> - private List<string> GetClaimTypeValues(string type, IEnumerable<Claim> claims) + private static List<string> GetClaimTypeValues(string type, IEnumerable<Claim> claims) { List<string> toReturn = new(); diff --git a/src/DevHive.Services/Services/FeedService.cs b/src/Services/DevHive.Services/Services/FeedService.cs index f2f4a3a..9c622b3 100644 --- a/src/DevHive.Services/Services/FeedService.cs +++ b/src/Services/DevHive.Services/Services/FeedService.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using AutoMapper; -using DevHive.Data.Interfaces.Repositories; +using DevHive.Common.Constants; +using DevHive.Data.Interfaces; using DevHive.Data.Models; using DevHive.Services.Interfaces; using DevHive.Services.Models; @@ -26,27 +28,25 @@ namespace DevHive.Services.Services /// <summary> /// This method is used in the feed page. - /// See the FeedRepository "GetFriendsPosts" menthod for more information on how it works. - /// </summary> - public async Task<ReadPageServiceModel> GetPage(GetPageServiceModel model) + /// See the FeedRepository "GetFriendsPosts" method for more information on how it works. + /// </summary> + public async Task<ReadPageServiceModel> GetPage(GetPageServiceModel getPageServiceModel) { + //TODO: Rework the initialization of User User user = null; - if (model.UserId != Guid.Empty) - user = await this._userRepository.GetByIdAsync(model.UserId); - else if (!string.IsNullOrEmpty(model.Username)) - user = await this._userRepository.GetByUsernameAsync(model.Username); + if (getPageServiceModel.UserId != Guid.Empty) + user = await this._userRepository.GetByIdAsync(getPageServiceModel.UserId); + else if (!string.IsNullOrEmpty(getPageServiceModel.Username)) + user = await this._userRepository.GetByUsernameAsync(getPageServiceModel.Username); else - throw new ArgumentException("Invalid given data!"); + throw new InvalidDataException(string.Format(ErrorMessages.InvalidData, ClassesConstants.Data.ToLower())); if (user == null) - throw new ArgumentException("User doesn't exist!"); - - if (user.Friends.Count == 0) - throw new ArgumentException("User has no friends to get feed from!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); List<Post> posts = await this._feedRepository - .GetFriendsPosts(user.Friends.ToList(), model.FirstRequestIssued, model.PageNumber, model.PageSize); + .GetFriendsPosts(user.Friends.ToList(), getPageServiceModel.FirstRequestIssued, getPageServiceModel.PageNumber, getPageServiceModel.PageSize); ReadPageServiceModel readPageServiceModel = new(); foreach (Post post in posts) @@ -57,19 +57,20 @@ namespace DevHive.Services.Services /// <summary> /// This method is used in the profile pages. - /// See the FeedRepository "GetUsersPosts" menthod for more information on how it works. - /// </summary> + /// See the FeedRepository "GetUsersPosts" method for more information on how it works. + /// </summary> public async Task<ReadPageServiceModel> GetUserPage(GetPageServiceModel model) { + //TODO: Rework the initialization of User User user = null; if (!string.IsNullOrEmpty(model.Username)) user = await this._userRepository.GetByUsernameAsync(model.Username); else - throw new ArgumentException("Invalid given data!"); + throw new InvalidDataException(string.Format(ErrorMessages.InvalidData, ClassesConstants.Data.ToLower())); if (user == null) - throw new ArgumentException("User doesn't exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); List<Post> posts = await this._feedRepository .GetUsersPosts(user, model.FirstRequestIssued, model.PageNumber, model.PageSize); diff --git a/src/Services/DevHive.Services/Services/FriendsService.cs b/src/Services/DevHive.Services/Services/FriendsService.cs new file mode 100644 index 0000000..98f654b --- /dev/null +++ b/src/Services/DevHive.Services/Services/FriendsService.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading.Tasks; +using DevHive.Common.Constants; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Interfaces; + +namespace DevHive.Services.Services +{ + public class FriendsService : IFriendsService + { + private readonly IUserRepository _friendRepository; + + public FriendsService(IUserRepository friendRepository) + { + this._friendRepository = friendRepository; + } + + public async Task<bool> AddFriend(Guid userId, string friendUsername) + { + User user = await this._friendRepository.GetByIdAsync(userId) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, nameof(user))); + + User friend = await this._friendRepository.GetByUsernameAsync(friendUsername) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, nameof(friend))); + + bool addedToUser = user.Friends.Add(friend) && await this._friendRepository.EditAsync(userId, user); + bool addedToFriend = friend.Friends.Add(user) && await this._friendRepository.EditAsync(friend.Id, friend); + return addedToUser && addedToFriend; + } + + public async Task<bool> RemoveFriend(Guid userId, string friendUsername) + { + User user = await this._friendRepository.GetByIdAsync(userId) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, nameof(user))); + + User friend = await this._friendRepository.GetByUsernameAsync(friendUsername) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, nameof(friend))); + + bool addedToUser = user.Friends.Remove(friend) && await this._friendRepository.EditAsync(userId, user); + bool addedToFriend = friend.Friends.Remove(user) && await this._friendRepository.EditAsync(friend.Id, friend); + return addedToUser && addedToFriend; + } + } +} diff --git a/src/DevHive.Services/Services/LanguageService.cs b/src/Services/DevHive.Services/Services/LanguageService.cs index a6364d8..7ee7d9f 100644 --- a/src/DevHive.Services/Services/LanguageService.cs +++ b/src/Services/DevHive.Services/Services/LanguageService.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Data; using System.Threading.Tasks; using AutoMapper; -using DevHive.Data.Interfaces.Repositories; +using DevHive.Common.Constants; +using DevHive.Data.Interfaces; using DevHive.Data.Models; using DevHive.Services.Interfaces; using DevHive.Services.Models.Language; @@ -24,7 +26,7 @@ namespace DevHive.Services.Services public async Task<Guid> CreateLanguage(CreateLanguageServiceModel createLanguageServiceModel) { if (await this._languageRepository.DoesLanguageNameExistAsync(createLanguageServiceModel.Name)) - throw new ArgumentException("Language already exists!"); + throw new DuplicateNameException(string.Format(ErrorMessages.AlreadyExists, ClassesConstants.Language)); Language language = this._languageMapper.Map<Language>(createLanguageServiceModel); bool success = await this._languageRepository.AddAsync(language); @@ -45,7 +47,7 @@ namespace DevHive.Services.Services Language language = await this._languageRepository.GetByIdAsync(id); if (language == null) - throw new ArgumentException("The language does not exist"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Language)); return this._languageMapper.Map<ReadLanguageServiceModel>(language); } @@ -65,10 +67,10 @@ namespace DevHive.Services.Services bool newLangNameExists = await this._languageRepository.DoesLanguageNameExistAsync(languageServiceModel.Name); if (!langExists) - throw new ArgumentException("Language does not exist!"); + throw new DuplicateNameException(string.Format(ErrorMessages.AlreadyExists, ClassesConstants.Language)); if (newLangNameExists) - throw new ArgumentException("Language name already exists in our data base!"); + throw new DuplicateNameException(string.Format(ErrorMessages.AlreadyExists, ClassesConstants.Language)); Language lang = this._languageMapper.Map<Language>(languageServiceModel); return await this._languageRepository.EditAsync(languageServiceModel.Id, lang); @@ -79,7 +81,7 @@ namespace DevHive.Services.Services public async Task<bool> DeleteLanguage(Guid id) { if (!await this._languageRepository.DoesLanguageExistAsync(id)) - throw new ArgumentException("Language does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Language)); Language language = await this._languageRepository.GetByIdAsync(id); return await this._languageRepository.DeleteAsync(language); diff --git a/src/DevHive.Services/Services/PostService.cs b/src/Services/DevHive.Services/Services/PostService.cs index fe91f23..8580e82 100644 --- a/src/DevHive.Services/Services/PostService.cs +++ b/src/Services/DevHive.Services/Services/PostService.cs @@ -1,19 +1,20 @@ using System; using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; using AutoMapper; +using DevHive.Common.Constants; +using DevHive.Data.Interfaces; using DevHive.Data.Models; -using DevHive.Services.Models.Post; -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; +using DevHive.Data.Models.Relational; using DevHive.Services.Interfaces; -using DevHive.Data.Interfaces.Repositories; -using System.Linq; -using DevHive.Data.RelationModels; +using DevHive.Services.Models.Post; namespace DevHive.Services.Services { - public class PostService : IPostService + public class PostService : IPostService { private readonly ICloudService _cloudService; private readonly IUserRepository _userRepository; @@ -34,14 +35,14 @@ namespace DevHive.Services.Services public async Task<Guid> CreatePost(CreatePostServiceModel createPostServiceModel) { if (!await this._userRepository.DoesUserExistAsync(createPostServiceModel.CreatorId)) - throw new ArgumentException("User does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); Post post = this._postMapper.Map<Post>(createPostServiceModel); if (createPostServiceModel.Files.Count != 0) { List<string> fileUrls = await _cloudService.UploadFilesToCloud(createPostServiceModel.Files); - post.Attachments = this.GetPostAttachmentsFromUrls(post, fileUrls); + post.Attachments = GetPostAttachmentsFromUrls(post, fileUrls); } post.Creator = await this._userRepository.GetByIdAsync(createPostServiceModel.CreatorId); @@ -66,7 +67,7 @@ namespace DevHive.Services.Services public async Task<ReadPostServiceModel> GetPostById(Guid id) { Post post = await this._postRepository.GetByIdAsync(id) ?? - throw new ArgumentException("The post does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Post)); // This can't happen in repo, because of how time is usually compared post.Comments = post.Comments @@ -74,13 +75,23 @@ namespace DevHive.Services.Services .ToList(); User user = await this._userRepository.GetByIdAsync(post.Creator.Id) ?? - throw new ArgumentException("The user does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); + + int currentRating = 0; + foreach (Rating rating in post.Ratings) + { + if (rating.IsLike) + currentRating++; + else + currentRating--; + } ReadPostServiceModel readPostServiceModel = this._postMapper.Map<ReadPostServiceModel>(post); readPostServiceModel.CreatorFirstName = user.FirstName; readPostServiceModel.CreatorLastName = user.LastName; readPostServiceModel.CreatorUsername = user.UserName; readPostServiceModel.FileUrls = post.Attachments.Select(x => x.FileUrl).ToList(); + readPostServiceModel.CurrentRating = currentRating; return readPostServiceModel; } @@ -90,7 +101,7 @@ namespace DevHive.Services.Services public async Task<Guid> UpdatePost(UpdatePostServiceModel updatePostServiceModel) { if (!await this._postRepository.DoesPostExist(updatePostServiceModel.PostId)) - throw new ArgumentException("Post does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Post)); Post post = this._postMapper.Map<Post>(updatePostServiceModel); @@ -101,12 +112,12 @@ namespace DevHive.Services.Services List<string> fileUrlsToRemove = await this._postRepository.GetFileUrls(updatePostServiceModel.PostId); bool success = await _cloudService.RemoveFilesFromCloud(fileUrlsToRemove); if (!success) - throw new InvalidCastException("Could not delete files from the post!"); + throw new InvalidOperationException(string.Format(ErrorMessages.CannotDelete, ClassesConstants.Files.ToLower())); } List<string> fileUrls = await _cloudService.UploadFilesToCloud(updatePostServiceModel.Files) ?? - throw new ArgumentNullException("Unable to upload images to cloud"); - post.Attachments = this.GetPostAttachmentsFromUrls(post, fileUrls); + throw new InvalidOperationException(string.Format(ErrorMessages.CannotUpload, ClassesConstants.Files.ToLower())); + post.Attachments = GetPostAttachmentsFromUrls(post, fileUrls); } post.Creator = await this._userRepository.GetByIdAsync(updatePostServiceModel.CreatorId); @@ -126,7 +137,7 @@ namespace DevHive.Services.Services public async Task<bool> DeletePost(Guid id) { if (!await this._postRepository.DoesPostExist(id)) - throw new ArgumentException("Post does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Post)); Post post = await this._postRepository.GetByIdAsync(id); @@ -135,7 +146,7 @@ namespace DevHive.Services.Services List<string> fileUrls = await this._postRepository.GetFileUrls(id); bool success = await _cloudService.RemoveFilesFromCloud(fileUrls); if (!success) - throw new InvalidCastException("Could not delete files from the post. Please try again"); + throw new InvalidOperationException(string.Format(ErrorMessages.CannotDelete, ClassesConstants.Files.ToLower())); } return await this._postRepository.DeleteAsync(post); @@ -144,25 +155,25 @@ namespace DevHive.Services.Services #region Validations /// <summary> - /// Checks whether the user Id in the token and the given user Id match - /// </summary> + /// Checks whether the user Id in the token and the given user Id match + /// </summary> public async Task<bool> ValidateJwtForCreating(Guid userId, string rawTokenData) { - User user = await this.GetUserForValidation(rawTokenData); + User user = await GetUserForValidation(rawTokenData); return user.Id == userId; } /// <summary> - /// Checks whether the post, gotten with the postId, + /// Checks whether the post, gotten with the postId, /// is made by the user in the token /// or if the user in the token is an admin - /// </summary> + /// </summary> public async Task<bool> ValidateJwtForPost(Guid postId, string rawTokenData) { Post post = await this._postRepository.GetByIdAsync(postId) ?? - throw new ArgumentException("Post does not exist!"); - User user = await this.GetUserForValidation(rawTokenData); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Post)); + User user = await GetUserForValidation(rawTokenData); //If user made the post if (post.Creator.Id == user.Id) @@ -175,15 +186,15 @@ namespace DevHive.Services.Services } /// <summary> - /// Checks whether the comment, gotten with the commentId, + /// Checks whether the comment, gotten with the commentId, /// is made by the user in the token /// or if the user in the token is an admin - /// </summary> + /// </summary> public async Task<bool> ValidateJwtForComment(Guid commentId, string rawTokenData) { Comment comment = await this._commentRepository.GetByIdAsync(commentId) ?? - throw new ArgumentException("Comment does not exist!"); - User user = await this.GetUserForValidation(rawTokenData); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Comment)); + User user = await GetUserForValidation(rawTokenData); //If user made the comment if (comment.Creator.Id == user.Id) @@ -196,25 +207,24 @@ namespace DevHive.Services.Services } /// <summary> - /// Returns the user, via their Id in the token - /// </summary> + /// Returns the user, via their Id in the token + /// </summary> private async Task<User> GetUserForValidation(string rawTokenData) { JwtSecurityToken jwt = new JwtSecurityTokenHandler().ReadJwtToken(rawTokenData.Remove(0, 7)); - Guid jwtUserId = Guid.Parse(this.GetClaimTypeValues("ID", jwt.Claims).First()); - //HashSet<string> jwtRoleNames = this.GetClaimTypeValues("role", jwt.Claims); + Guid jwtUserId = Guid.Parse(GetClaimTypeValues("ID", jwt.Claims).First()); User user = await this._userRepository.GetByIdAsync(jwtUserId) ?? - throw new ArgumentException("User does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); return user; } /// <summary> - /// Returns all values from a given claim type - /// </summary> - private List<string> GetClaimTypeValues(string type, IEnumerable<Claim> claims) + /// Returns all values from a given claim type + /// </summary> + private static List<string> GetClaimTypeValues(string type, IEnumerable<Claim> claims) { List<string> toReturn = new(); @@ -227,9 +237,9 @@ namespace DevHive.Services.Services #endregion #region Misc - private List<PostAttachments> GetPostAttachmentsFromUrls(Post post, List<string> fileUrls) + private static List<PostAttachments> GetPostAttachmentsFromUrls(Post post, List<string> fileUrls) { - List<PostAttachments> postAttachments = new List<PostAttachments>(); + List<PostAttachments> postAttachments = new(); foreach (string url in fileUrls) postAttachments.Add(new PostAttachments { Post = post, FileUrl = url }); return postAttachments; diff --git a/src/Services/DevHive.Services/Services/ProfilePictureService.cs b/src/Services/DevHive.Services/Services/ProfilePictureService.cs new file mode 100644 index 0000000..cafa7cb --- /dev/null +++ b/src/Services/DevHive.Services/Services/ProfilePictureService.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using DevHive.Common.Constants; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.ProfilePicture; +using Microsoft.AspNetCore.Http; + +namespace DevHive.Services.Services +{ + public class ProfilePictureService : IProfilePictureService + { + private readonly IUserRepository _userRepository; + private readonly IProfilePictureRepository _profilePictureRepository; + private readonly ICloudService _cloudinaryService; + + public ProfilePictureService(IUserRepository userRepository, IProfilePictureRepository profilePictureRepository, ICloudService cloudinaryService) + { + this._userRepository = userRepository; + this._profilePictureRepository = profilePictureRepository; + this._cloudinaryService = cloudinaryService; + } + + public async Task<string> GetProfilePictureById(Guid id) + { + return (await this._profilePictureRepository.GetByIdAsync(id)).PictureURL; + } + + public async Task<string> UpdateProfilePicture(ProfilePictureServiceModel profilePictureServiceModel) + { + ValidateProfPic(profilePictureServiceModel.ProfilePictureFormFile); + await ValidateUserExistsAsync(profilePictureServiceModel.UserId); + + User user = await this._userRepository.GetByIdAsync(profilePictureServiceModel.UserId); + if (user.ProfilePicture.Id != Guid.Empty) + { + List<string> file = new() { user.ProfilePicture.PictureURL }; + bool removed = await this._cloudinaryService.RemoveFilesFromCloud(file); + + if (!removed) + throw new InvalidOperationException(string.Format(ErrorMessages.CannotDelete, ClassesConstants.Picture.ToLower())); + } + + return await SaveProfilePictureInDatabase(profilePictureServiceModel); + } + + public async Task<bool> DeleteProfilePicture(Guid id) + { + ProfilePicture profilePic = await this._profilePictureRepository.GetByIdAsync(id) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Picture)); + + bool removedFromDb = await this._profilePictureRepository.DeleteAsync(profilePic); + if (!removedFromDb) + throw new InvalidOperationException(string.Format(ErrorMessages.CannotDelete, ClassesConstants.Picture.ToLower())); + + List<string> file = new() { profilePic.PictureURL }; + bool removedFromCloud = await this._cloudinaryService.RemoveFilesFromCloud(file); + if (!removedFromCloud) + throw new InvalidOperationException(string.Format(ErrorMessages.CannotDelete, ClassesConstants.Picture.ToLower())); + + return true; + } + + private async Task<string> SaveProfilePictureInDatabase(ProfilePictureServiceModel profilePictureServiceModel) + { + List<IFormFile> file = new() { profilePictureServiceModel.ProfilePictureFormFile }; + string picUrl = (await this._cloudinaryService.UploadFilesToCloud(file))[0]; + ProfilePicture profilePic = new() { PictureURL = picUrl }; + + User user = await this._userRepository.GetByIdAsync(profilePictureServiceModel.UserId); + profilePic.UserId = user.Id; + profilePic.User = user; + + bool success = await this._profilePictureRepository.AddAsync(profilePic); + if (!success) + throw new InvalidOperationException(string.Format(ErrorMessages.CannotUpload, ClassesConstants.Files.ToLower())); + + user.ProfilePicture = profilePic; + bool userProfilePicAlter = await this._userRepository.EditAsync(user.Id, user); + + if (!userProfilePicAlter) + throw new InvalidOperationException(string.Format(ErrorMessages.CannotEdit, "user's profile picture")); + + return picUrl; + } + + private static void ValidateProfPic(IFormFile profilePictureFormFile) + { + if (profilePictureFormFile.Length == 0) + throw new ArgumentNullException(nameof(profilePictureFormFile), string.Format(ErrorMessages.InvalidData, ClassesConstants.Data.ToLower())); + } + + private async Task ValidateUserExistsAsync(Guid userId) + { + if (!await this._userRepository.DoesUserExistAsync(userId)) + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); + } + } +} diff --git a/src/Services/DevHive.Services/Services/RatingService.cs b/src/Services/DevHive.Services/Services/RatingService.cs new file mode 100644 index 0000000..d6b4299 --- /dev/null +++ b/src/Services/DevHive.Services/Services/RatingService.cs @@ -0,0 +1,120 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Common.Constants; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Rating; + +namespace DevHive.Services.Services +{ + public class RatingService : IRatingService + { + private readonly IPostRepository _postRepository; + private readonly IUserRepository _userRepository; + private readonly IRatingRepository _ratingRepository; + private readonly IMapper _mapper; + + private const string NotRated = "{0} has not rated" + ClassesConstants.Post; + + public RatingService(IPostRepository postRepository, IRatingRepository ratingRepository, IUserRepository userRepository, IMapper mapper) + { + this._postRepository = postRepository; + this._ratingRepository = ratingRepository; + this._userRepository = userRepository; + this._mapper = mapper; + } + + #region Create + public async Task<Guid> RatePost(CreateRatingServiceModel createRatingServiceModel) + { + if (!await this._postRepository.DoesPostExist(createRatingServiceModel.PostId)) + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Post)); + + if (await this._ratingRepository.UserRatedPost(createRatingServiceModel.UserId, createRatingServiceModel.PostId)) + throw new ArgumentException("User already rated the post!"); + + Rating rating = this._mapper.Map<Rating>(createRatingServiceModel); + + rating.User = await this._userRepository.GetByIdAsync(createRatingServiceModel.UserId); + rating.Post = await this._postRepository.GetByIdAsync(createRatingServiceModel.PostId); + + bool success = await this._ratingRepository.AddAsync(rating); + + if (success) + { + Rating newRating = await this._ratingRepository.GetRatingByUserAndPostId(rating.User.Id, rating.Post.Id); + + return newRating.Id; + } + else + return Guid.Empty; + } + #endregion + + #region Read + public async Task<ReadRatingServiceModel> GetRatingById(Guid ratingId) + { + Rating rating = await this._ratingRepository.GetByIdAsync(ratingId); + if (rating is null) + return null; + + ReadRatingServiceModel readRatingServiceModel = this._mapper.Map<ReadRatingServiceModel>(rating); + readRatingServiceModel.UserId = rating.User.Id; + + return readRatingServiceModel; + } + + public async Task<ReadRatingServiceModel> GetRatingByPostAndUser(Guid userId, Guid postId) + { + Rating rating = await this._ratingRepository.GetRatingByUserAndPostId(userId, postId); + if (rating is null) + return null; + + ReadRatingServiceModel readRatingServiceModel = this._mapper.Map<ReadRatingServiceModel>(rating); + readRatingServiceModel.UserId = rating.User.Id; + + return readRatingServiceModel; + } + #endregion + + #region Update + public async Task<ReadRatingServiceModel> UpdateRating(UpdateRatingServiceModel updateRatingServiceModel) + { + Rating rating = await this._ratingRepository.GetRatingByUserAndPostId(updateRatingServiceModel.UserId, updateRatingServiceModel.PostId) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Rating)); + + User user = await this._userRepository.GetByIdAsync(updateRatingServiceModel.UserId) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); + + if (!await this._ratingRepository.UserRatedPost(updateRatingServiceModel.UserId, updateRatingServiceModel.PostId)) + throw new ArgumentException(string.Format(NotRated, ClassesConstants.User)); + + rating.User = user; + rating.IsLike = updateRatingServiceModel.IsLike; + + bool result = await this._ratingRepository.EditAsync(updateRatingServiceModel.Id, rating); + + if (result) + { + ReadRatingServiceModel readRatingServiceModel = this._mapper.Map<ReadRatingServiceModel>(rating); + return readRatingServiceModel; + } + else + return null; + } + #endregion + + #region Delete + public async Task<bool> DeleteRating(Guid ratingId) + { + if (!await this._ratingRepository.DoesRatingExist(ratingId)) + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Rating)); + + Rating rating = await this._ratingRepository.GetByIdAsync(ratingId); + return await this._ratingRepository.DeleteAsync(rating); + } + #endregion + } +} diff --git a/src/DevHive.Services/Services/RoleService.cs b/src/Services/DevHive.Services/Services/RoleService.cs index a8b8e17..f61181a 100644 --- a/src/DevHive.Services/Services/RoleService.cs +++ b/src/Services/DevHive.Services/Services/RoleService.cs @@ -1,11 +1,12 @@ using System; +using System.Data; using System.Threading.Tasks; using AutoMapper; -using DevHive.Data.Interfaces.Repositories; +using DevHive.Common.Constants; +using DevHive.Data.Interfaces; using DevHive.Data.Models; using DevHive.Services.Interfaces; -using DevHive.Services.Models.Identity.Role; -using DevHive.Services.Models.Language; +using DevHive.Services.Models.Role; namespace DevHive.Services.Services { @@ -20,17 +21,17 @@ namespace DevHive.Services.Services this._roleMapper = mapper; } - public async Task<Guid> CreateRole(CreateRoleServiceModel roleServiceModel) + public async Task<Guid> CreateRole(CreateRoleServiceModel createRoleServiceModel) { - if (await this._roleRepository.DoesNameExist(roleServiceModel.Name)) - throw new ArgumentException("Role already exists!"); + if (await this._roleRepository.DoesNameExist(createRoleServiceModel.Name)) + throw new DuplicateNameException(string.Format(ErrorMessages.AlreadyExists, ClassesConstants.Role)); - Role role = this._roleMapper.Map<Role>(roleServiceModel); + Role role = this._roleMapper.Map<Role>(createRoleServiceModel); bool success = await this._roleRepository.AddAsync(role); if (success) { - Role newRole = await this._roleRepository.GetByNameAsync(roleServiceModel.Name); + Role newRole = await this._roleRepository.GetByNameAsync(createRoleServiceModel.Name); return newRole.Id; } else @@ -40,8 +41,8 @@ namespace DevHive.Services.Services public async Task<RoleServiceModel> GetRoleById(Guid id) { - Role role = await this._roleRepository.GetByIdAsync(id) - ?? throw new ArgumentException("Role does not exist!"); + Role role = await this._roleRepository.GetByIdAsync(id) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Role)); return this._roleMapper.Map<RoleServiceModel>(role); } @@ -49,10 +50,10 @@ namespace DevHive.Services.Services public async Task<bool> UpdateRole(UpdateRoleServiceModel updateRoleServiceModel) { if (!await this._roleRepository.DoesRoleExist(updateRoleServiceModel.Id)) - throw new ArgumentException("Role does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Role)); if (await this._roleRepository.DoesNameExist(updateRoleServiceModel.Name)) - throw new ArgumentException("Role name already exists!"); + throw new DuplicateNameException(string.Format(ErrorMessages.AlreadyExists, ClassesConstants.Role)); Role role = this._roleMapper.Map<Role>(updateRoleServiceModel); return await this._roleRepository.EditAsync(updateRoleServiceModel.Id, role); @@ -61,7 +62,7 @@ namespace DevHive.Services.Services public async Task<bool> DeleteRole(Guid id) { if (!await this._roleRepository.DoesRoleExist(id)) - throw new ArgumentException("Role does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Role)); Role role = await this._roleRepository.GetByIdAsync(id); return await this._roleRepository.DeleteAsync(role); diff --git a/src/DevHive.Services/Services/TechnologyService.cs b/src/Services/DevHive.Services/Services/TechnologyService.cs index 6dd6286..4cf84c5 100644 --- a/src/DevHive.Services/Services/TechnologyService.cs +++ b/src/Services/DevHive.Services/Services/TechnologyService.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Data; using System.Threading.Tasks; using AutoMapper; -using DevHive.Data.Interfaces.Repositories; +using DevHive.Common.Constants; +using DevHive.Data.Interfaces; using DevHive.Data.Models; using DevHive.Services.Interfaces; using DevHive.Services.Models.Technology; @@ -24,7 +26,7 @@ namespace DevHive.Services.Services public async Task<Guid> CreateTechnology(CreateTechnologyServiceModel technologyServiceModel) { if (await this._technologyRepository.DoesTechnologyNameExistAsync(technologyServiceModel.Name)) - throw new ArgumentException("Technology already exists!"); + throw new DuplicateNameException(string.Format(ErrorMessages.AlreadyExists, ClassesConstants.Technology)); Technology technology = this._technologyMapper.Map<Technology>(technologyServiceModel); bool success = await this._technologyRepository.AddAsync(technology); @@ -45,7 +47,7 @@ namespace DevHive.Services.Services Technology technology = await this._technologyRepository.GetByIdAsync(id); if (technology == null) - throw new ArgumentException("The technology does not exist"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Technology)); return this._technologyMapper.Map<ReadTechnologyServiceModel>(technology); } @@ -62,10 +64,10 @@ namespace DevHive.Services.Services public async Task<bool> UpdateTechnology(UpdateTechnologyServiceModel updateTechnologyServiceModel) { if (!await this._technologyRepository.DoesTechnologyExistAsync(updateTechnologyServiceModel.Id)) - throw new ArgumentException("Technology does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Technology)); if (await this._technologyRepository.DoesTechnologyNameExistAsync(updateTechnologyServiceModel.Name)) - throw new ArgumentException("Technology name already exists!"); + throw new DuplicateNameException(string.Format(ErrorMessages.AlreadyExists, ClassesConstants.Technology)); Technology technology = this._technologyMapper.Map<Technology>(updateTechnologyServiceModel); bool result = await this._technologyRepository.EditAsync(updateTechnologyServiceModel.Id, technology); @@ -78,7 +80,7 @@ namespace DevHive.Services.Services public async Task<bool> DeleteTechnology(Guid id) { if (!await this._technologyRepository.DoesTechnologyExistAsync(id)) - throw new ArgumentException("Technology does not exist!"); + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Technology)); Technology technology = await this._technologyRepository.GetByIdAsync(id); bool result = await this._technologyRepository.DeleteAsync(technology); diff --git a/src/Services/DevHive.Services/Services/UserService.cs b/src/Services/DevHive.Services/Services/UserService.cs new file mode 100644 index 0000000..62576d4 --- /dev/null +++ b/src/Services/DevHive.Services/Services/UserService.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Common.Constants; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Common.Models.Identity; +using DevHive.Data.Interfaces; +using DevHive.Data.Models; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.User; + +namespace DevHive.Services.Services +{ + public class UserService : IUserService + { + private readonly IUserRepository _userRepository; + private readonly IRoleRepository _roleRepository; + private readonly ILanguageRepository _languageRepository; + private readonly ITechnologyRepository _technologyRepository; + private readonly IMapper _userMapper; + private readonly IJwtService _jwtService; + + private const string NoYourselfAsFriend = "You cant add yourself as a friend(sry, bro)!"; + private const string Rant = "Can't promote shit in this country..."; + + + public UserService(IUserRepository userRepository, + ILanguageRepository languageRepository, + IRoleRepository roleRepository, + ITechnologyRepository technologyRepository, + IMapper mapper, + IJwtService jwtService) + { + this._userRepository = userRepository; + this._roleRepository = roleRepository; + this._userMapper = mapper; + this._languageRepository = languageRepository; + this._technologyRepository = technologyRepository; + this._jwtService = jwtService; + } + + #region Authentication + public async Task<TokenModel> LoginUser(LoginServiceModel loginModel) + { + if (!await this._userRepository.DoesUsernameExistAsync(loginModel.UserName)) + throw new InvalidDataException(string.Format(ErrorMessages.InvalidData, ClassesConstants.Username)); + + User user = await this._userRepository.GetByUsernameAsync(loginModel.UserName); + + if (!await this._userRepository.VerifyPassword(user, loginModel.Password)) + throw new InvalidDataException(string.Format(ErrorMessages.IncorrectData, ClassesConstants.Password.ToLower())); + + List<string> roleNames = user.Roles.Select(x => x.Name).ToList(); + return new TokenModel(this._jwtService.GenerateJwtToken(user.Id, user.UserName, roleNames)); + } + + public async Task<TokenModel> RegisterUser(RegisterServiceModel registerModel) + { + if (await this._userRepository.DoesUsernameExistAsync(registerModel.UserName)) + throw new DuplicateNameException(string.Format(ErrorMessages.AlreadyExists, ClassesConstants.Username)); + + if (await this._userRepository.DoesEmailExistAsync(registerModel.Email)) + throw new DuplicateNameException(string.Format(ErrorMessages.AlreadyExists, ClassesConstants.Email)); + + + User user = this._userMapper.Map<User>(registerModel); + + bool userResult = await this._userRepository.AddAsync(user); + bool roleResult = await this._userRepository.AddRoleToUser(user, Role.DefaultRole); + + if (!userResult) + throw new InvalidOperationException(string.Format(ErrorMessages.CannotCreate, ClassesConstants.User.ToLower())); + if (!roleResult) + throw new InvalidOperationException(string.Format(ErrorMessages.CannotAdd, ClassesConstants.Role.ToLower())); + + User createdUser = await this._userRepository.GetByUsernameAsync(registerModel.UserName); + + List<string> roleNames = createdUser.Roles.Select(x => x.Name).ToList(); + return new TokenModel(this._jwtService.GenerateJwtToken(createdUser.Id, createdUser.UserName, roleNames)); + } + #endregion + + #region Read + public async Task<UserServiceModel> GetUserById(Guid id) + { + User user = await this._userRepository.GetByIdAsync(id) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); + + return this._userMapper.Map<UserServiceModel>(user); + } + + public async Task<UserServiceModel> GetUserByUsername(string username) + { + User user = await this._userRepository.GetByUsernameAsync(username) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); + + return this._userMapper.Map<UserServiceModel>(user); + } + #endregion + + #region Update + public async Task<UserServiceModel> UpdateUser(UpdateUserServiceModel updateUserServiceModel) + { + await ValidateUserOnUpdate(updateUserServiceModel); + + User user = await this._userRepository.GetByIdAsync(updateUserServiceModel.Id); + await PopulateUserModel(user, updateUserServiceModel); + + if (updateUserServiceModel.Friends.Count > 0) + await CreateRelationToFriends(user, updateUserServiceModel.Friends.ToList()); + else + user.Friends.Clear(); + + bool result = await this._userRepository.EditAsync(user.Id, user); + + if (!result) + throw new InvalidOperationException(string.Format(ErrorMessages.CannotEdit, ClassesConstants.User.ToLower())); + + User newUser = await this._userRepository.GetByIdAsync(user.Id); + return this._userMapper.Map<UserServiceModel>(newUser); + } + #endregion + + #region Delete + public async Task<bool> DeleteUser(Guid id) + { + if (!await this._userRepository.DoesUserExistAsync(id)) + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); + + User user = await this._userRepository.GetByIdAsync(id); + return await this._userRepository.DeleteAsync(user); + } + #endregion + + #region Validations + /// <summary> + /// Checks whether the user in the model exists and whether the username in the model is already taken. + /// If the check fails (is false), it throws an exception, otherwise nothing happens + /// </summary> + private async Task ValidateUserOnUpdate(UpdateUserServiceModel updateUserServiceModel) + { + if (!await this._userRepository.DoesUserExistAsync(updateUserServiceModel.Id)) + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User)); + + if (updateUserServiceModel.Friends.Any(x => x.UserName == updateUserServiceModel.UserName)) + throw new InvalidOperationException(NoYourselfAsFriend); + + if (!await this._userRepository.DoesUserHaveThisUsernameAsync(updateUserServiceModel.Id, updateUserServiceModel.UserName) + && await this._userRepository.DoesUsernameExistAsync(updateUserServiceModel.UserName)) + throw new DuplicateNameException(string.Format(ErrorMessages.AlreadyExists, ClassesConstants.Username.ToLower())); + + List<string> usernames = new(); + foreach (var friend in updateUserServiceModel.Friends) + usernames.Add(friend.UserName); + + if (!await this._userRepository.ValidateFriendsCollectionAsync(usernames)) + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.OneOrMoreFriends)); + } + #endregion + + #region Misc + public async Task<TokenModel> SuperSecretPromotionToAdmin(Guid userId) + { + User user = await this._userRepository.GetByIdAsync(userId) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.User) + " " + Rant); + + if (!await this._roleRepository.DoesNameExist(Role.AdminRole)) + { + Role adminRole = new() { Name = Role.AdminRole }; + adminRole.Users.Add(user); + + await this._roleRepository.AddAsync(adminRole); + } + + Role admin = await this._roleRepository.GetByNameAsync(Role.AdminRole); + + user.Roles.Add(admin); + await this._userRepository.EditAsync(user.Id, user); + + User createdUser = await this._userRepository.GetByIdAsync(userId); + List<string> roleNames = createdUser + .Roles + .Select(x => x.Name) + .ToList(); + + return new TokenModel(this._jwtService.GenerateJwtToken(createdUser.Id, createdUser.UserName, roleNames)); + } + + private async Task PopulateUserModel(User user, UpdateUserServiceModel updateUserServiceModel) + { + user.UserName = updateUserServiceModel.UserName; + user.FirstName = updateUserServiceModel.FirstName; + user.LastName = updateUserServiceModel.LastName; + user.Email = updateUserServiceModel.Email; + + //Do NOT allow a user to change his roles, unless he is an Admin + bool isAdmin = await this._userRepository.IsInRoleAsync(user, Role.AdminRole); + + if (isAdmin) + { + HashSet<Role> roles = new(); + foreach (var role in updateUserServiceModel.Roles) + { + Role returnedRole = await this._roleRepository.GetByNameAsync(role.Name) ?? + throw new ArgumentNullException(string.Format(ErrorMessages.DoesNotExist, ClassesConstants.Role)); + + roles.Add(returnedRole); + } + user.Roles = roles; + } + + HashSet<Language> languages = new(); + int languagesCount = updateUserServiceModel.Languages.Count; + for (int i = 0; i < languagesCount; i++) + { + Language language = await this._languageRepository.GetByNameAsync(updateUserServiceModel.Languages.ElementAt(i).Name) ?? + throw new InvalidDataException(string.Format(ErrorMessages.InvalidData, nameof(Language))); + + languages.Add(language); + } + user.Languages = languages; + + /* Fetch Technologies and replace model's*/ + HashSet<Technology> technologies = new(); + int technologiesCount = updateUserServiceModel.Technologies.Count; + for (int i = 0; i < technologiesCount; i++) + { + Technology technology = await this._technologyRepository.GetByNameAsync(updateUserServiceModel.Technologies.ElementAt(i).Name) ?? + throw new InvalidDataException(string.Format(ErrorMessages.InvalidData, nameof(Technology))); + + + technologies.Add(technology); + } + user.Technologies = technologies; + } + + private async Task CreateRelationToFriends(User user, List<UpdateFriendServiceModel> friends) + { + foreach (var friend in friends) + { + User amigo = await this._userRepository.GetByUsernameAsync(friend.UserName); + + user.Friends.Add(amigo); + amigo.Friends.Add(user); + + await this._userRepository.EditAsync(amigo.Id, amigo); + } + } + #endregion + } +} diff --git a/src/DevHive.Web/Attributes/GoodPasswordModelValidation.cs b/src/Web/DevHive.Web.Models/Attributes/GoodPasswordAttribute.cs index 7d6a1ea..c452499 100644 --- a/src/DevHive.Web/Attributes/GoodPasswordModelValidation.cs +++ b/src/Web/DevHive.Web.Models/Attributes/GoodPasswordAttribute.cs @@ -1,9 +1,8 @@ -using System; using System.ComponentModel.DataAnnotations; -namespace DevHive.Web.Attributes +namespace DevHive.Web.Models.Attributes { - public class GoodPassword : ValidationAttribute + public class GoodPasswordAttribute : ValidationAttribute { public override bool IsValid(object value) { @@ -11,7 +10,7 @@ namespace DevHive.Web.Attributes for (int i = 0; i < stringValue.Length; i++) { - if (Char.IsDigit(stringValue[i])) + if (char.IsDigit(stringValue[i])) { base.ErrorMessage = "Password must be atleast 5 characters long!"; return stringValue.Length >= 5; diff --git a/src/DevHive.Web/Attributes/OnlyLettersModelValidation.cs b/src/Web/DevHive.Web.Models/Attributes/OnlyLettersModelValidation.cs index 07afee9..faaeee4 100644 --- a/src/DevHive.Web/Attributes/OnlyLettersModelValidation.cs +++ b/src/Web/DevHive.Web.Models/Attributes/OnlyLettersModelValidation.cs @@ -1,9 +1,9 @@ using System; using System.ComponentModel.DataAnnotations; -namespace DevHive.Web.Attributes +namespace DevHive.Web.Models.Attributes { - public class OnlyLetters : ValidationAttribute + public class OnlyLettersAttribute : ValidationAttribute { public override bool IsValid(object value) { @@ -11,7 +11,7 @@ namespace DevHive.Web.Attributes foreach (char ch in stringValue) { - if (!Char.IsLetter(ch)) + if (!char.IsLetter(ch)) return false; } return true; diff --git a/src/DevHive.Web/Models/Comment/CreateCommentWebModel.cs b/src/Web/DevHive.Web.Models/Comment/CreateCommentWebModel.cs index 8b2bf8d..8b2bf8d 100644 --- a/src/DevHive.Web/Models/Comment/CreateCommentWebModel.cs +++ b/src/Web/DevHive.Web.Models/Comment/CreateCommentWebModel.cs diff --git a/src/DevHive.Web/Models/Comment/ReadCommentWebModel.cs b/src/Web/DevHive.Web.Models/Comment/ReadCommentWebModel.cs index 4d3aff7..4d3aff7 100644 --- a/src/DevHive.Web/Models/Comment/ReadCommentWebModel.cs +++ b/src/Web/DevHive.Web.Models/Comment/ReadCommentWebModel.cs diff --git a/src/DevHive.Web/Models/Comment/UpdateCommentWebModel.cs b/src/Web/DevHive.Web.Models/Comment/UpdateCommentWebModel.cs index b5d7970..b5d7970 100644 --- a/src/DevHive.Web/Models/Comment/UpdateCommentWebModel.cs +++ b/src/Web/DevHive.Web.Models/Comment/UpdateCommentWebModel.cs diff --git a/src/Web/DevHive.Web.Models/DevHive.Web.Models.csproj b/src/Web/DevHive.Web.Models/DevHive.Web.Models.csproj new file mode 100644 index 0000000..79c856f --- /dev/null +++ b/src/Web/DevHive.Web.Models/DevHive.Web.Models.csproj @@ -0,0 +1,13 @@ +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + <ItemGroup> + <ProjectReference Include="..\..\Common\DevHive.Common\DevHive.Common.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common.Models\DevHive.Common.Models.csproj"/> + </ItemGroup> + <ItemGroup> + <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/DevHive.Web/Models/Feed/GetPageWebModel.cs b/src/Web/DevHive.Web.Models/Feed/GetPageWebModel.cs index 4ea44cc..4ea44cc 100644 --- a/src/DevHive.Web/Models/Feed/GetPageWebModel.cs +++ b/src/Web/DevHive.Web.Models/Feed/GetPageWebModel.cs diff --git a/src/DevHive.Web/Models/Feed/ReadPageWebModel.cs b/src/Web/DevHive.Web.Models/Feed/ReadPageWebModel.cs index f429313..f429313 100644 --- a/src/DevHive.Web/Models/Feed/ReadPageWebModel.cs +++ b/src/Web/DevHive.Web.Models/Feed/ReadPageWebModel.cs diff --git a/src/DevHive.Web/Models/Language/CreateLanguageWebModel.cs b/src/Web/DevHive.Web.Models/Language/CreateLanguageWebModel.cs index a739e7f..a739e7f 100644 --- a/src/DevHive.Web/Models/Language/CreateLanguageWebModel.cs +++ b/src/Web/DevHive.Web.Models/Language/CreateLanguageWebModel.cs diff --git a/src/DevHive.Web/Models/Language/LanguageWebModel.cs b/src/Web/DevHive.Web.Models/Language/LanguageWebModel.cs index 7515e70..7515e70 100644 --- a/src/DevHive.Web/Models/Language/LanguageWebModel.cs +++ b/src/Web/DevHive.Web.Models/Language/LanguageWebModel.cs diff --git a/src/DevHive.Web/Models/Language/ReadLanguageWebModel.cs b/src/Web/DevHive.Web.Models/Language/ReadLanguageWebModel.cs index 3d9d5b6..3d9d5b6 100644 --- a/src/DevHive.Web/Models/Language/ReadLanguageWebModel.cs +++ b/src/Web/DevHive.Web.Models/Language/ReadLanguageWebModel.cs diff --git a/src/DevHive.Web/Models/Language/UpdateLanguageWebModel.cs b/src/Web/DevHive.Web.Models/Language/UpdateLanguageWebModel.cs index 128d534..128d534 100644 --- a/src/DevHive.Web/Models/Language/UpdateLanguageWebModel.cs +++ b/src/Web/DevHive.Web.Models/Language/UpdateLanguageWebModel.cs diff --git a/src/DevHive.Web/Models/Post/CreatePostWebModel.cs b/src/Web/DevHive.Web.Models/Post/CreatePostWebModel.cs index 237259d..237259d 100644 --- a/src/DevHive.Web/Models/Post/CreatePostWebModel.cs +++ b/src/Web/DevHive.Web.Models/Post/CreatePostWebModel.cs diff --git a/src/DevHive.Web/Models/Post/ReadPostWebModel.cs b/src/Web/DevHive.Web.Models/Post/ReadPostWebModel.cs index 8238f47..d6ea1f4 100644 --- a/src/DevHive.Web/Models/Post/ReadPostWebModel.cs +++ b/src/Web/DevHive.Web.Models/Post/ReadPostWebModel.cs @@ -21,6 +21,7 @@ namespace DevHive.Web.Models.Post public List<IdModel> Comments { get; set; } public List<string> FileUrls { get; set; } - // public List<FileContentResult> Files { get; set; } + + public int CurrentRating { get; set; } } } diff --git a/src/DevHive.Web/Models/Post/UpdatePostWebModel.cs b/src/Web/DevHive.Web.Models/Post/UpdatePostWebModel.cs index a0c9b61..a0c9b61 100644 --- a/src/DevHive.Web/Models/Post/UpdatePostWebModel.cs +++ b/src/Web/DevHive.Web.Models/Post/UpdatePostWebModel.cs diff --git a/src/Web/DevHive.Web.Models/ProfilePicture/ProfilePictureWebModel.cs b/src/Web/DevHive.Web.Models/ProfilePicture/ProfilePictureWebModel.cs new file mode 100644 index 0000000..3fe201c --- /dev/null +++ b/src/Web/DevHive.Web.Models/ProfilePicture/ProfilePictureWebModel.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Http; + +namespace DevHive.Web.Models.ProfilePicture +{ + public class ProfilePictureWebModel + { + public IFormFile Picture { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Rating/CreateRatingWebModel.cs b/src/Web/DevHive.Web.Models/Rating/CreateRatingWebModel.cs new file mode 100644 index 0000000..abbb702 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Rating/CreateRatingWebModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace DevHive.Web.Models.Rating +{ + public class CreateRatingWebModel + { + public Guid PostId { get; set; } + + public bool IsLike { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Rating/ReadRatingWebModel.cs b/src/Web/DevHive.Web.Models/Rating/ReadRatingWebModel.cs new file mode 100644 index 0000000..40f4c6f --- /dev/null +++ b/src/Web/DevHive.Web.Models/Rating/ReadRatingWebModel.cs @@ -0,0 +1,15 @@ +using System; + +namespace DevHive.Web.Models.Rating +{ + public class ReadRatingWebModel + { + public Guid Id { get; set; } + + public Guid PostId { get; set; } + + public Guid UserId { get; set; } + + public bool IsLike { get; set; } + } +} diff --git a/src/Web/DevHive.Web.Models/Rating/UpdateRatingWebModel.cs b/src/Web/DevHive.Web.Models/Rating/UpdateRatingWebModel.cs new file mode 100644 index 0000000..176c099 --- /dev/null +++ b/src/Web/DevHive.Web.Models/Rating/UpdateRatingWebModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DevHive.Web.Models.Rating +{ + public class UpdateRatingWebModel + { + public bool IsLike { get; set; } + } +} diff --git a/src/DevHive.Web/Models/Identity/Role/CreateRoleWebModel.cs b/src/Web/DevHive.Web.Models/Role/CreateRoleWebModel.cs index 859cdd9..9beced4 100644 --- a/src/DevHive.Web/Models/Identity/Role/CreateRoleWebModel.cs +++ b/src/Web/DevHive.Web.Models/Role/CreateRoleWebModel.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -namespace DevHive.Web.Models.Identity.Role +namespace DevHive.Web.Models.Role { public class CreateRoleWebModel { diff --git a/src/DevHive.Web/Models/Identity/Role/RoleWebModel.cs b/src/Web/DevHive.Web.Models/Role/RoleWebModel.cs index 99b0f50..680fb05 100644 --- a/src/DevHive.Web/Models/Identity/Role/RoleWebModel.cs +++ b/src/Web/DevHive.Web.Models/Role/RoleWebModel.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -namespace DevHive.Web.Models.Identity.Role +namespace DevHive.Web.Models.Role { public class RoleWebModel { diff --git a/src/DevHive.Web/Models/Identity/Role/UpdateRoleWebModel.cs b/src/Web/DevHive.Web.Models/Role/UpdateRoleWebModel.cs index 3870481..f32839e 100644 --- a/src/DevHive.Web/Models/Identity/Role/UpdateRoleWebModel.cs +++ b/src/Web/DevHive.Web.Models/Role/UpdateRoleWebModel.cs @@ -2,7 +2,7 @@ using System; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -namespace DevHive.Web.Models.Identity.Role +namespace DevHive.Web.Models.Role { public class UpdateRoleWebModel { diff --git a/src/DevHive.Web/Models/Technology/CreateTechnologyWebModel.cs b/src/Web/DevHive.Web.Models/Technology/CreateTechnologyWebModel.cs index ec9ec15..ec9ec15 100644 --- a/src/DevHive.Web/Models/Technology/CreateTechnologyWebModel.cs +++ b/src/Web/DevHive.Web.Models/Technology/CreateTechnologyWebModel.cs diff --git a/src/DevHive.Web/Models/Technology/ReadTechnologyWebModel.cs b/src/Web/DevHive.Web.Models/Technology/ReadTechnologyWebModel.cs index 94542d7..94542d7 100644 --- a/src/DevHive.Web/Models/Technology/ReadTechnologyWebModel.cs +++ b/src/Web/DevHive.Web.Models/Technology/ReadTechnologyWebModel.cs diff --git a/src/DevHive.Web/Models/Technology/TechnologyWebModel.cs b/src/Web/DevHive.Web.Models/Technology/TechnologyWebModel.cs index 6e8273b..6e8273b 100644 --- a/src/DevHive.Web/Models/Technology/TechnologyWebModel.cs +++ b/src/Web/DevHive.Web.Models/Technology/TechnologyWebModel.cs diff --git a/src/DevHive.Web/Models/Technology/UpdateTechnologyWebModel.cs b/src/Web/DevHive.Web.Models/Technology/UpdateTechnologyWebModel.cs index 3e9fe2a..3e9fe2a 100644 --- a/src/DevHive.Web/Models/Technology/UpdateTechnologyWebModel.cs +++ b/src/Web/DevHive.Web.Models/Technology/UpdateTechnologyWebModel.cs diff --git a/src/DevHive.Web/Models/Identity/User/BaseUserWebModel.cs b/src/Web/DevHive.Web.Models/User/BaseUserWebModel.cs index 297e1a5..5fdb757 100644 --- a/src/DevHive.Web/Models/Identity/User/BaseUserWebModel.cs +++ b/src/Web/DevHive.Web.Models/User/BaseUserWebModel.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -using DevHive.Web.Attributes; +using DevHive.Web.Models.Attributes; -namespace DevHive.Web.Models.Identity.User +namespace DevHive.Web.Models.User { public class BaseUserWebModel { diff --git a/src/DevHive.Web/Models/Identity/User/LoginWebModel.cs b/src/Web/DevHive.Web.Models/User/LoginWebModel.cs index ccd806f..7d4e93a 100644 --- a/src/DevHive.Web/Models/Identity/User/LoginWebModel.cs +++ b/src/Web/DevHive.Web.Models/User/LoginWebModel.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -using DevHive.Web.Attributes; +using DevHive.Web.Models.Attributes; -namespace DevHive.Web.Models.Identity.User +namespace DevHive.Web.Models.User { public class LoginWebModel { diff --git a/src/DevHive.Web/Models/Identity/User/RegisterWebModel.cs b/src/Web/DevHive.Web.Models/User/RegisterWebModel.cs index 0fc7ec6..999ff00 100644 --- a/src/DevHive.Web/Models/Identity/User/RegisterWebModel.cs +++ b/src/Web/DevHive.Web.Models/User/RegisterWebModel.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -using DevHive.Web.Attributes; +using DevHive.Web.Models.Attributes; -namespace DevHive.Web.Models.Identity.User +namespace DevHive.Web.Models.User { public class RegisterWebModel : BaseUserWebModel { diff --git a/src/DevHive.Web/Models/Identity/User/TokenWebModel.cs b/src/Web/DevHive.Web.Models/User/TokenWebModel.cs index 154b64b..7f3361d 100644 --- a/src/DevHive.Web/Models/Identity/User/TokenWebModel.cs +++ b/src/Web/DevHive.Web.Models/User/TokenWebModel.cs @@ -1,4 +1,4 @@ -namespace DevHive.Web.Models.Identity.User +namespace DevHive.Web.Models.User { public class TokenWebModel { @@ -9,4 +9,4 @@ namespace DevHive.Web.Models.Identity.User public string Token { get; set; } } -}
\ No newline at end of file +} diff --git a/src/DevHive.Web/Models/Identity/User/UpdateUserWebModel.cs b/src/Web/DevHive.Web.Models/User/UpdateUserWebModel.cs index 62901f6..f74927e 100644 --- a/src/DevHive.Web/Models/Identity/User/UpdateUserWebModel.cs +++ b/src/Web/DevHive.Web.Models/User/UpdateUserWebModel.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -using DevHive.Web.Attributes; -using DevHive.Web.Models.Identity.Role; +using DevHive.Web.Models.Attributes; +using DevHive.Web.Models.Role; using DevHive.Web.Models.Language; using DevHive.Web.Models.Technology; -namespace DevHive.Web.Models.Identity.User +namespace DevHive.Web.Models.User { public class UpdateUserWebModel : BaseUserWebModel { diff --git a/src/DevHive.Web/Models/Identity/User/UserWebModel.cs b/src/Web/DevHive.Web.Models/User/UserWebModel.cs index 7ab8cca..37141d0 100644 --- a/src/DevHive.Web/Models/Identity/User/UserWebModel.cs +++ b/src/Web/DevHive.Web.Models/User/UserWebModel.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using DevHive.Common.Models.Misc; -using DevHive.Web.Models.Identity.Role; +using DevHive.Web.Models.Role; using DevHive.Web.Models.Language; using DevHive.Web.Models.Technology; -namespace DevHive.Web.Models.Identity.User +namespace DevHive.Web.Models.User { public class UserWebModel : BaseUserWebModel { diff --git a/src/DevHive.Web/Models/Identity/User/UsernameWebModel.cs b/src/Web/DevHive.Web.Models/User/UsernameWebModel.cs index c533bba..638cb15 100644 --- a/src/DevHive.Web/Models/Identity/User/UsernameWebModel.cs +++ b/src/Web/DevHive.Web.Models/User/UsernameWebModel.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; -using DevHive.Web.Attributes; +using DevHive.Web.Models.Attributes; -namespace DevHive.Web.Models.Identity.User +namespace DevHive.Web.Models.User { public class UsernameWebModel { diff --git a/src/Web/DevHive.Web.Tests/CommentController.Tests.cs b/src/Web/DevHive.Web.Tests/CommentController.Tests.cs new file mode 100644 index 0000000..830677e --- /dev/null +++ b/src/Web/DevHive.Web.Tests/CommentController.Tests.cs @@ -0,0 +1,325 @@ +using System; +using System.Linq; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Comment; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Comment; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class CommentControllerTests + { + const string MESSAGE = "Gosho Trapov"; + private Mock<ICommentService> _commentServiceMock; + private Mock<IMapper> _mapperMock; + private Mock<IJwtService> _jwtServiceMock; + private CommentController _commentController; + + #region Setup + [SetUp] + public void SetUp() + { + this._commentServiceMock = new Mock<ICommentService>(); + this._mapperMock = new Mock<IMapper>(); + this._jwtServiceMock = new Mock<IJwtService>(); + this._commentController = new CommentController(this._commentServiceMock.Object, this._mapperMock.Object, this._jwtServiceMock.Object); + } + #endregion + + #region Add + [Test] + public void AddComment_ReturnsOkObjectResult_WhenCommentIsSuccessfullyCreated() + { + Guid id = Guid.NewGuid(); + CreateCommentWebModel createCommentWebModel = new() + { + Message = MESSAGE + }; + CreateCommentServiceModel createCommentServiceModel = new() + { + Message = MESSAGE + }; + + this._mapperMock + .Setup(p => p.Map<CreateCommentServiceModel>(It.IsAny<CreateCommentWebModel>())) + .Returns(createCommentServiceModel); + this._commentServiceMock + .Setup(p => p.AddComment(It.IsAny<CreateCommentServiceModel>())) + .ReturnsAsync(id); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._commentController.AddComment(Guid.NewGuid(), createCommentWebModel, null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(id, resultId); + } + + [Test] + public void AddComment_ReturnsBadRequestObjectResult_WhenCommentIsNotCreatedSuccessfully() + { + CreateCommentWebModel createCommentWebModel = new() + { + Message = MESSAGE + }; + CreateCommentServiceModel createCommentServiceModel = new() + { + Message = MESSAGE + }; + string errorMessage = $"Could not create comment!"; + + + this._mapperMock + .Setup(p => p.Map<CreateCommentServiceModel>(It.IsAny<CreateCommentWebModel>())) + .Returns(createCommentServiceModel); + this._commentServiceMock + .Setup(p => p.AddComment(It.IsAny<CreateCommentServiceModel>())) + .ReturnsAsync(Guid.Empty); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._commentController.AddComment(Guid.NewGuid(), createCommentWebModel, null).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultMessage = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(errorMessage, resultMessage); + } + + [Test] + public void AddComment_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() + { + CreateCommentWebModel createCommentWebModel = new() + { + Message = MESSAGE + }; + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(false); + + IActionResult result = this._commentController.AddComment(Guid.NewGuid(), createCommentWebModel, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsTheComment_WhenItExists() + { + Guid id = Guid.NewGuid(); + ReadCommentServiceModel readCommentServiceModel = new() + { + Message = MESSAGE + }; + ReadCommentWebModel readCommentWebModel = new() + { + Message = MESSAGE + }; + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.GetCommentById(It.IsAny<Guid>())) + .ReturnsAsync(readCommentServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadCommentWebModel>(It.IsAny<ReadCommentServiceModel>())) + .Returns(readCommentWebModel); + + IActionResult result = this._commentController.GetCommentById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadCommentWebModel resultModel = okObjectResult.Value as ReadCommentWebModel; + + Assert.AreEqual(MESSAGE, resultModel.Message); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenCommentIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateCommentWebModel updateCommentWebModel = new() + { + NewMessage = MESSAGE + }; + UpdateCommentServiceModel updateCommentServiceModel = new() + { + NewMessage = MESSAGE + }; + + this._commentServiceMock + .Setup(p => p.UpdateComment(It.IsAny<UpdateCommentServiceModel>())) + .ReturnsAsync(id); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<UpdateCommentServiceModel>(It.IsAny<UpdateCommentWebModel>())) + .Returns(updateCommentServiceModel); + + IActionResult result = this._commentController.UpdateComment(Guid.Empty, updateCommentWebModel, null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + object resultModel = okObjectResult.Value; + string[] resultAsString = resultModel.ToString().Split(' ').ToArray(); + + Assert.AreEqual(id.ToString(), resultAsString[3]); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenCommentIsNotUpdatedSuccessfully() + { + string message = "Unable to update comment!"; + UpdateCommentWebModel updateCommentWebModel = new() + { + NewMessage = MESSAGE + }; + UpdateCommentServiceModel updateCommentServiceModel = new() + { + NewMessage = MESSAGE + }; + + this._commentServiceMock + .Setup(p => p.UpdateComment(It.IsAny<UpdateCommentServiceModel>())) + .ReturnsAsync(Guid.Empty); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<UpdateCommentServiceModel>(It.IsAny<UpdateCommentWebModel>())) + .Returns(updateCommentServiceModel); + + IActionResult result = this._commentController.UpdateComment(Guid.Empty, updateCommentWebModel, null).Result; + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + + [Test] + public void Update_ShouldReturnUnauthorizedResult_WhenUserIsNotAuthorized() + { + UpdateCommentWebModel updateCommentWebModel = new() + { + NewMessage = MESSAGE + }; + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(false); + // this.CommentServiceMock.Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())).ReturnsAsync(false)); + + IActionResult result = this._commentController.UpdateComment(Guid.Empty, updateCommentWebModel, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenCommentIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._commentServiceMock + .Setup(p => p.DeleteComment(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._commentController.DeleteComment(id, null).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void DeleteComment_ReturnsBadRequestObjectResult_WhenCommentIsNotDeletedSuccessfully() + { + string message = "Could not delete Comment"; + Guid id = Guid.NewGuid(); + + this._commentServiceMock + .Setup(p => p.DeleteComment(It.IsAny<Guid>())) + .ReturnsAsync(false); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._commentController.DeleteComment(id, null).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + + [Test] + public void DeleteComment_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() + { + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._commentServiceMock + .Setup(p => p.ValidateJwtForComment(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(false); + + IActionResult result = this._commentController.DeleteComment(Guid.Empty, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + } +} diff --git a/src/DevHive.Tests/DevHive.Web.Tests/DevHive.Web.Tests.csproj b/src/Web/DevHive.Web.Tests/DevHive.Web.Tests.csproj index a171e0b..49a9173 100644 --- a/src/DevHive.Tests/DevHive.Web.Tests/DevHive.Web.Tests.csproj +++ b/src/Web/DevHive.Web.Tests/DevHive.Web.Tests.csproj @@ -1,24 +1,22 @@ <Project Sdk="Microsoft.NET.Sdk"> - <PropertyGroup> <TargetFramework>net5.0</TargetFramework> - <IsPackable>false</IsPackable> </PropertyGroup> - <ItemGroup> - <PackageReference Include="Moq" Version="4.16.0" /> - <PackageReference Include="NUnit" Version="3.13.0" /> - <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" /> + <PackageReference Include="Moq" Version="4.16.1"/> + <PackageReference Include="NUnit" Version="3.13.1"/> + <PackageReference Include="NUnit3TestAdapter" Version="3.17.0"/> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> </ItemGroup> - <ItemGroup> - <ProjectReference Include="..\..\DevHive.Web\DevHive.Web.csproj" /> + <ProjectReference Include="..\DevHive.Web\DevHive.Web.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common\DevHive.Common.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common.Models\DevHive.Common.Models.csproj"/> </ItemGroup> - <PropertyGroup> <EnableNETAnalyzers>true</EnableNETAnalyzers> <AnalysisLevel>latest</AnalysisLevel> </PropertyGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/src/Web/DevHive.Web.Tests/FeedController.Tests.cs b/src/Web/DevHive.Web.Tests/FeedController.Tests.cs new file mode 100644 index 0000000..2f43b6c --- /dev/null +++ b/src/Web/DevHive.Web.Tests/FeedController.Tests.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Services.Interfaces; +using DevHive.Services.Models; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Comment; +using DevHive.Web.Models.Feed; +using DevHive.Web.Models.Post; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class FeedControllerTests + { + private Mock<IFeedService> _feedServiceMock; + private Mock<IMapper> _mapperMock; + private FeedController _feedController; + + #region SetUp + [SetUp] + public void SetUp() + { + this._feedServiceMock = new Mock<IFeedService>(); + this._mapperMock = new Mock<IMapper>(); + this._feedController = new FeedController(this._feedServiceMock.Object, this._mapperMock.Object); + } + #endregion + + #region GetPosts + [Test] + public async Task GetPosts_ReturnsOkObjectResultWithCorrectReadPageWebModel_WhenPostsExist() + { + GetPageWebModel getPageWebModel = new(); + GetPageServiceModel getPageServiceModel = new(); + ReadPageServiceModel readPageServiceModel = new(); + ReadPageWebModel readPageWebModel = new() + { + Posts = new() + { + new ReadPostWebModel(), + new ReadPostWebModel(), + new ReadPostWebModel() + } + }; + + this._feedServiceMock + .Setup(p => p.GetPage(It.IsAny<GetPageServiceModel>())) + .ReturnsAsync(readPageServiceModel); + this._mapperMock + .Setup(p => p.Map<GetPageServiceModel>(It.IsAny<GetPageWebModel>())) + .Returns(getPageServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadPageWebModel>(It.IsAny<ReadPageServiceModel>())) + .Returns(readPageWebModel); + + IActionResult result = await this._feedController.GetPosts(Guid.Empty, getPageWebModel); + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadPageWebModel resultModel = okObjectResult.Value as Models.Comment.ReadPageWebModel; + + Assert.AreEqual(3, resultModel.Posts.Count); + } + #endregion + + #region GetUserPosts + [Test] + public async Task GetUserPosts_GetsPostsOfUser_WhenTheyExist() + { + GetPageWebModel getPageWebModel = new(); + GetPageServiceModel getPageServiceModel = new(); + ReadPageServiceModel readPageServiceModel = new(); + ReadPageWebModel readPageWebModel = new() + { + Posts = new() + { + new ReadPostWebModel(), + new ReadPostWebModel(), + new ReadPostWebModel() + } + }; + + this._feedServiceMock + .Setup(p => p.GetUserPage(It.IsAny<GetPageServiceModel>())) + .ReturnsAsync(readPageServiceModel); + this._mapperMock + .Setup(p => p.Map<GetPageServiceModel>(It.IsAny<GetPageWebModel>())) + .Returns(getPageServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadPageWebModel>(It.IsAny<ReadPageServiceModel>())) + .Returns(readPageWebModel); + + IActionResult result = await this._feedController.GetUserPosts(null, getPageWebModel); + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadPageWebModel resultModel = okObjectResult.Value as Models.Comment.ReadPageWebModel; + + Assert.AreEqual(3, resultModel.Posts.Count); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/LanguageController.Tests.cs b/src/Web/DevHive.Web.Tests/LanguageController.Tests.cs new file mode 100644 index 0000000..52e07fa --- /dev/null +++ b/src/Web/DevHive.Web.Tests/LanguageController.Tests.cs @@ -0,0 +1,224 @@ +using System; +using System.Linq; +using AutoMapper; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Language; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Language; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class LanguageControllerTests + { + const string NAME = "Gosho Trapov"; + private Mock<ILanguageService> _languageServiceMock; + private Mock<IMapper> _mapperMock; + private LanguageController _languageController; + + [SetUp] + public void SetUp() + { + this._languageServiceMock = new Mock<ILanguageService>(); + this._mapperMock = new Mock<IMapper>(); + this._languageController = new LanguageController(this._languageServiceMock.Object, this._mapperMock.Object); + } + + #region Create + [Test] + public void CreateLanguage_ReturnsOkObjectResult_WhenLanguageIsSuccessfullyCreated() + { + CreateLanguageWebModel createLanguageWebModel = new() + { + Name = NAME + }; + CreateLanguageServiceModel createLanguageServiceModel = new() + { + Name = NAME + }; + Guid id = Guid.NewGuid(); + + this._mapperMock + .Setup(p => p.Map<CreateLanguageServiceModel>(It.IsAny<CreateLanguageWebModel>())) + .Returns(createLanguageServiceModel); + this._languageServiceMock + .Setup(p => p.CreateLanguage(It.IsAny<CreateLanguageServiceModel>())) + .ReturnsAsync(id); + + IActionResult result = this._languageController.Create(createLanguageWebModel).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(id, resultId); + } + + [Test] + public void CreateLanguage_ReturnsBadRequestObjectResult_WhenLanguageIsNotCreatedSuccessfully() + { + CreateLanguageWebModel createTechnologyWebModel = new() + { + Name = NAME + }; + CreateLanguageServiceModel createTechnologyServiceModel = new() + { + Name = NAME + }; + Guid id = Guid.Empty; + string errorMessage = $"Could not create language {NAME}"; + + this._mapperMock + .Setup(p => p.Map<CreateLanguageServiceModel>(It.IsAny<CreateLanguageWebModel>())) + .Returns(createTechnologyServiceModel); + this._languageServiceMock + .Setup(p => p.CreateLanguage(It.IsAny<CreateLanguageServiceModel>())) + .ReturnsAsync(id); + + IActionResult result = this._languageController.Create(createTechnologyWebModel).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultMessage = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(errorMessage, resultMessage); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsTheLanguage_WhenItExists() + { + Guid id = Guid.NewGuid(); + + ReadLanguageServiceModel readLanguageServiceModel = new() + { + Name = NAME + }; + ReadLanguageWebModel readLanguageWebModel = new() + { + Name = NAME + }; + + this._languageServiceMock + .Setup(p => p.GetLanguageById(It.IsAny<Guid>())) + .ReturnsAsync(readLanguageServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadLanguageWebModel>(It.IsAny<ReadLanguageServiceModel>())) + .Returns(readLanguageWebModel); + + IActionResult result = this._languageController.GetById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadLanguageWebModel resultModel = okObjectResult.Value as Models.Language.ReadLanguageWebModel; + + Assert.AreEqual(NAME, resultModel.Name); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenLanguageIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateLanguageWebModel updateLanguageWebModel = new() + { + Name = NAME + }; + UpdateLanguageServiceModel updateLanguageServiceModel = new() + { + Name = NAME + }; + + this._languageServiceMock + .Setup(p => p.UpdateLanguage(It.IsAny<UpdateLanguageServiceModel>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<UpdateLanguageServiceModel>(It.IsAny<UpdateLanguageWebModel>())) + .Returns(updateLanguageServiceModel); + + IActionResult result = this._languageController.Update(id, updateLanguageWebModel).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenLanguageIsNotUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + string message = "Could not update Language"; + UpdateLanguageWebModel updateLanguageWebModel = new() + { + Name = NAME + }; + UpdateLanguageServiceModel updateLanguageServiceModel = new() + { + Name = NAME + }; + + this._languageServiceMock + .Setup(p => p.UpdateLanguage(It.IsAny<UpdateLanguageServiceModel>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<UpdateLanguageServiceModel>(It.IsAny<UpdateLanguageWebModel>())) + .Returns(updateLanguageServiceModel); + + IActionResult result = this._languageController.Update(id, updateLanguageWebModel).Result; + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenLanguageIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._languageServiceMock + .Setup(p => p.DeleteLanguage(It.IsAny<Guid>())) + .ReturnsAsync(true); + + IActionResult result = this._languageController.Delete(id).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delet_ReturnsBadRequestObjectResult_WhenLanguageIsNotDeletedSuccessfully() + { + string message = "Could not delete Language"; + Guid id = Guid.NewGuid(); + + this._languageServiceMock + .Setup(p => p.DeleteLanguage(It.IsAny<Guid>())) + .ReturnsAsync(false); + + IActionResult result = this._languageController.Delete(id).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/PostController.Tests.cs b/src/Web/DevHive.Web.Tests/PostController.Tests.cs new file mode 100644 index 0000000..df95191 --- /dev/null +++ b/src/Web/DevHive.Web.Tests/PostController.Tests.cs @@ -0,0 +1,309 @@ +using System; +using System.Linq; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Post; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Post; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class PostControllerTests + { + const string MESSAGE = "Gosho Trapov"; + private Mock<IPostService> _postServiceMock; + private Mock<IMapper> _mapperMock; + private Mock<IJwtService> _jwtServiceMock; + private PostController _postController; + + [SetUp] + public void SetUp() + { + this._postServiceMock = new Mock<IPostService>(); + this._mapperMock = new Mock<IMapper>(); + this._jwtServiceMock = new Mock<IJwtService>(); + this._postController = new PostController(this._postServiceMock.Object, this._mapperMock.Object, this._jwtServiceMock.Object); + } + + #region Create + [Test] + public void CreatePost_ReturnsOkObjectResult_WhenPostIsSuccessfullyCreated() + { + CreatePostWebModel createPostWebModel = new() + { + Message = MESSAGE + }; + CreatePostServiceModel createPostServiceModel = new() + { + Message = MESSAGE + }; + Guid id = Guid.NewGuid(); + + this._mapperMock + .Setup(p => p.Map<CreatePostServiceModel>(It.IsAny<CreatePostWebModel>())) + .Returns(createPostServiceModel); + this._postServiceMock + .Setup(p => p.CreatePost(It.IsAny<CreatePostServiceModel>())) + .ReturnsAsync(id); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Create(Guid.Empty, createPostWebModel, null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(id, resultId); + } + + [Test] + public void CreatePost_ReturnsBadRequestObjectResult_WhenPostIsNotCreatedSuccessfully() + { + CreatePostWebModel createTechnologyWebModel = new() + { + Message = MESSAGE + }; + CreatePostServiceModel createTechnologyServiceModel = new() + { + Message = MESSAGE + }; + Guid id = Guid.Empty; + string errorMessage = $"Could not create post!"; + + this._mapperMock + .Setup(p => p.Map<CreatePostServiceModel>(It.IsAny<CreatePostWebModel>())) + .Returns(createTechnologyServiceModel); + this._postServiceMock + .Setup(p => p.CreatePost(It.IsAny<CreatePostServiceModel>())) + .ReturnsAsync(id); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Create(Guid.Empty, createTechnologyWebModel, null).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultMessage = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(errorMessage, resultMessage); + } + + [Test] + public void CreatePost_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() + { + CreatePostWebModel createPostWebModel = new() + { + Message = MESSAGE + }; + + this._postServiceMock + .Setup(p => p.ValidateJwtForCreating(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(false); + + IActionResult result = this._postController.Create(Guid.NewGuid(), createPostWebModel, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsThePost_WhenItExists() + { + Guid id = Guid.NewGuid(); + + ReadPostServiceModel readPostServiceModel = new() + { + Message = MESSAGE + }; + ReadPostWebModel readPostWebModel = new() + { + Message = MESSAGE + }; + + this._postServiceMock + .Setup(p => p.GetPostById(It.IsAny<Guid>())) + .ReturnsAsync(readPostServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadPostWebModel>(It.IsAny<ReadPostServiceModel>())) + .Returns(readPostWebModel); + + IActionResult result = this._postController.GetById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + ReadPostWebModel resultModel = okObjectResult.Value as Models.Post.ReadPostWebModel; + + Assert.AreEqual(MESSAGE, resultModel.Message); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenPostIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdatePostWebModel updatePostWebModel = new() + { + NewMessage = MESSAGE + }; + UpdatePostServiceModel updatePostServiceModel = new() + { + NewMessage = MESSAGE + }; + + this._postServiceMock + .Setup(p => p.UpdatePost(It.IsAny<UpdatePostServiceModel>())) + .ReturnsAsync(id); + this._mapperMock + .Setup(p => p.Map<UpdatePostServiceModel>(It.IsAny<UpdatePostWebModel>())) + .Returns(updatePostServiceModel); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Update(id, updatePostWebModel, null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenPostIsNotUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + string message = "Could not update post!"; + UpdatePostWebModel updatePostWebModel = new() + { + NewMessage = MESSAGE + }; + UpdatePostServiceModel updatePostServiceModel = new() + { + NewMessage = MESSAGE + }; + + this._postServiceMock + .Setup(p => p.UpdatePost(It.IsAny<UpdatePostServiceModel>())) + .ReturnsAsync(Guid.Empty); + this._mapperMock + .Setup(p => p.Map<UpdatePostServiceModel>(It.IsAny<UpdatePostWebModel>())) + .Returns(updatePostServiceModel); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Update(id, updatePostWebModel, null).Result; + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + + [Test] + public void Update_ShouldReturnUnauthorizedResult_WhenUserIsNotAuthorized() + { + UpdatePostWebModel updatePostWebModel = new() + { + NewMessage = MESSAGE + }; + + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(false); + + IActionResult result = this._postController.Update(Guid.Empty, updatePostWebModel, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenPostIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._postServiceMock + .Setup(p => p.DeletePost(It.IsAny<Guid>())) + .ReturnsAsync(true); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Delete(id, null).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delete_ReturnsBadRequestObjectResult_WhenPostIsNotDeletedSuccessfully() + { + string message = "Could not delete Post"; + Guid id = Guid.NewGuid(); + + this._postServiceMock + .Setup(p => p.DeletePost(It.IsAny<Guid>())) + .ReturnsAsync(false); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(true); + + IActionResult result = this._postController.Delete(id, null).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + + [Test] + public void DeletePost_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() + { + this._postServiceMock + .Setup(p => p.ValidateJwtForPost(It.IsAny<Guid>(), It.IsAny<string>())) + .ReturnsAsync(false); + + IActionResult result = this._postController.Delete(Guid.Empty, null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/RatingController.Tests.cs b/src/Web/DevHive.Web.Tests/RatingController.Tests.cs new file mode 100644 index 0000000..c7340a6 --- /dev/null +++ b/src/Web/DevHive.Web.Tests/RatingController.Tests.cs @@ -0,0 +1,299 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Rating; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Rating; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class RatingControllerTests + { + private Mock<IRatingService> _ratingServiceMock; + private Mock<IMapper> _mapperMock; + private Mock<IJwtService> _jwtServiceMock; + private RatingController _ratingController; + + [SetUp] + public void SetUp() + { + this._ratingServiceMock = new Mock<IRatingService>(); + this._mapperMock = new Mock<IMapper>(); + this._jwtServiceMock = new Mock<IJwtService>(); + this._ratingController = new RatingController(this._ratingServiceMock.Object, this._mapperMock.Object, this._jwtServiceMock.Object); + } + + #region Create + [Test] + public void CreateRating_ReturnsOkObjectResult_WhenRatingIsSuccessfullyCreated() + { + Guid postId = Guid.NewGuid(); + CreateRatingWebModel createRatingWebModel = new CreateRatingWebModel + { + PostId = postId, + IsLike = true + }; + CreateRatingServiceModel createRatingServiceModel = new CreateRatingServiceModel + { + PostId = postId, + IsLike = true + }; + Guid ratingId = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<CreateRatingServiceModel>(It.IsAny<CreateRatingWebModel>())) + .Returns(createRatingServiceModel); + this._ratingServiceMock + .Setup(p => p.RatePost(It.IsAny<CreateRatingServiceModel>())) + .ReturnsAsync(ratingId); + + IActionResult result = this._ratingController.RatePost(Guid.Empty, createRatingWebModel, String.Empty).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(ratingId, resultId); + } + + [Test] + public void CreateRating_ReturnsBadRequestResult_WhenRatingIsNotSuccessfullyCreated() + { + Guid postId = Guid.NewGuid(); + CreateRatingWebModel createRatingWebModel = new CreateRatingWebModel + { + PostId = postId, + IsLike = true + }; + CreateRatingServiceModel createRatingServiceModel = new CreateRatingServiceModel + { + PostId = postId, + IsLike = true + }; + Guid ratingId = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<CreateRatingServiceModel>(It.IsAny<CreateRatingWebModel>())) + .Returns(createRatingServiceModel); + this._ratingServiceMock + .Setup(p => p.RatePost(It.IsAny<CreateRatingServiceModel>())) + .ReturnsAsync(Guid.Empty); + + IActionResult result = this._ratingController.RatePost(Guid.Empty, createRatingWebModel, String.Empty).Result; + + Assert.IsInstanceOf<BadRequestResult>(result); + } + #endregion + + #region Read + [Test] + public void GetRatingById_ReturnsTheRating_WhenItExists() + { + Guid id = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + Guid postId = Guid.NewGuid(); + ReadRatingWebModel readRatingWebModel = new ReadRatingWebModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + ReadRatingServiceModel readRatingServiceModel = new ReadRatingServiceModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + + this._mapperMock + .Setup(p => p.Map<ReadRatingServiceModel>(It.IsAny<ReadRatingWebModel>())) + .Returns(readRatingServiceModel); + this._ratingServiceMock + .Setup(p => p.GetRatingById(It.IsAny<Guid>())) + .ReturnsAsync(readRatingServiceModel); + + IActionResult result = this._ratingController.GetRatingById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + } + + [Test] + public void GetRatingByUserAndPost_ReturnsTheRating_WhenItExists() + { + Guid id = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + Guid postId = Guid.NewGuid(); + ReadRatingWebModel readRatingWebModel = new ReadRatingWebModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + ReadRatingServiceModel readRatingServiceModel = new ReadRatingServiceModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + + this._mapperMock + .Setup(p => p.Map<ReadRatingServiceModel>(It.IsAny<ReadRatingWebModel>())) + .Returns(readRatingServiceModel); + this._ratingServiceMock + .Setup(p => p.GetRatingByPostAndUser(It.IsAny<Guid>(), It.IsAny<Guid>())) + .ReturnsAsync(readRatingServiceModel); + + IActionResult result = this._ratingController.GetRatingByUserAndPost(userId, postId).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + } + + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenRatingIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + Guid postId = Guid.NewGuid(); + UpdateRatingWebModel updateRatingWebModel = new UpdateRatingWebModel + { + IsLike = true + }; + UpdateRatingServiceModel updateRatingServiceModel = new UpdateRatingServiceModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + ReadRatingWebModel readRatingWebModel = new ReadRatingWebModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + ReadRatingServiceModel readRatingServiceModel = new ReadRatingServiceModel + { + Id = id, + UserId = userId, + PostId = postId, + IsLike = true + }; + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<UpdateRatingServiceModel>(It.IsAny<UpdateRatingWebModel>())) + .Returns(updateRatingServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadRatingWebModel>(It.IsAny<ReadRatingServiceModel>())) + .Returns(readRatingWebModel); + this._ratingServiceMock + .Setup(p => p.UpdateRating(It.IsAny<UpdateRatingServiceModel>())) + .ReturnsAsync(readRatingServiceModel); + + IActionResult result = this._ratingController.UpdateRating(userId, postId, updateRatingWebModel, String.Empty).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenLanguageIsNotUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateRatingWebModel updateRatingWebModel = new UpdateRatingWebModel + { + IsLike = true + }; + UpdateRatingServiceModel updateRatingServiceModel = new UpdateRatingServiceModel + { + Id = id, + IsLike = true + }; + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<UpdateRatingServiceModel>(It.IsAny<UpdateRatingWebModel>())) + .Returns(updateRatingServiceModel); + this._ratingServiceMock + .Setup(p => p.UpdateRating(It.IsAny<UpdateRatingServiceModel>())) + .Returns(Task.FromResult<ReadRatingServiceModel>(null)); + + IActionResult result = this._ratingController.UpdateRating(Guid.Empty, Guid.Empty, updateRatingWebModel, String.Empty).Result; + + Assert.IsInstanceOf<BadRequestResult>(result); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenRatingIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._ratingServiceMock + .Setup(p => p.DeleteRating(It.IsAny<Guid>())) + .ReturnsAsync(true); + + IActionResult result = this._ratingController.DeleteRating(Guid.Empty, Guid.Empty, String.Empty).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delete_ReturnsBadRequestObjectResult_WhenRatingIsNotDeletedSuccessfully() + { + string message = "Could not delete Rating"; + Guid id = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._ratingServiceMock + .Setup(p => p.DeleteRating(It.IsAny<Guid>())) + .ReturnsAsync(false); + + IActionResult result = this._ratingController.DeleteRating(Guid.Empty, Guid.Empty, String.Empty).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + } +} diff --git a/src/Web/DevHive.Web.Tests/RoleController.Tests.cs b/src/Web/DevHive.Web.Tests/RoleController.Tests.cs new file mode 100644 index 0000000..d1c3381 --- /dev/null +++ b/src/Web/DevHive.Web.Tests/RoleController.Tests.cs @@ -0,0 +1,224 @@ +using System; +using System.Linq; +using AutoMapper; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Role; +using DevHive.Web.Controllers; +using DevHive.Web.Models.Role; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class RoleControllerTests + { + const string NAME = "Gosho Trapov"; + private Mock<IRoleService> _roleServiceMock; + private Mock<IMapper> _mapperMock; + private RoleController _roleController; + + [SetUp] + public void SetUp() + { + this._roleServiceMock = new Mock<IRoleService>(); + this._mapperMock = new Mock<IMapper>(); + this._roleController = new RoleController(this._roleServiceMock.Object, this._mapperMock.Object); + } + + #region Create + [Test] + public void CreateRole_ReturnsOkObjectResult_WhenRoleIsSuccessfullyCreated() + { + CreateRoleWebModel createRoleWebModel = new() + { + Name = NAME + }; + CreateRoleServiceModel createRoleServiceModel = new() + { + Name = NAME + }; + Guid id = Guid.NewGuid(); + + this._mapperMock + .Setup(p => p.Map<CreateRoleServiceModel>(It.IsAny<CreateRoleWebModel>())) + .Returns(createRoleServiceModel); + this._roleServiceMock + .Setup(p => p.CreateRole(It.IsAny<CreateRoleServiceModel>())) + .ReturnsAsync(id); + + IActionResult result = this._roleController.Create(createRoleWebModel).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var splitted = (result as OkObjectResult).Value + .ToString() + .Split('{', '}', '=', ' ') + .Where(x => !string.IsNullOrEmpty(x)) + .ToArray(); + + Guid resultId = Guid.Parse(splitted[1]); + + Assert.AreEqual(id, resultId); + } + + [Test] + public void CreateRole_ReturnsBadRequestObjectResult_WhenRoleIsNotCreatedSuccessfully() + { + CreateRoleWebModel createTechnologyWebModel = new() + { + Name = NAME + }; + CreateRoleServiceModel createTechnologyServiceModel = new() + { + Name = NAME + }; + Guid id = Guid.Empty; + string errorMessage = $"Could not create role {NAME}"; + + this._mapperMock + .Setup(p => p.Map<CreateRoleServiceModel>(It.IsAny<CreateRoleWebModel>())) + .Returns(createTechnologyServiceModel); + this._roleServiceMock + .Setup(p => p.CreateRole(It.IsAny<CreateRoleServiceModel>())) + .ReturnsAsync(id); + + IActionResult result = this._roleController.Create(createTechnologyWebModel).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultMessage = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(errorMessage, resultMessage); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsTheRole_WhenItExists() + { + Guid id = Guid.NewGuid(); + + RoleServiceModel roleServiceModel = new() + { + Name = NAME + }; + RoleWebModel roleWebModel = new() + { + Name = NAME + }; + + this._roleServiceMock + .Setup(p => p.GetRoleById(It.IsAny<Guid>())) + .ReturnsAsync(roleServiceModel); + this._mapperMock + .Setup(p => p.Map<RoleWebModel>(It.IsAny<RoleServiceModel>())) + .Returns(roleWebModel); + + IActionResult result = this._roleController.GetById(id).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + RoleWebModel resultModel = okObjectResult.Value as RoleWebModel; + + Assert.AreEqual(NAME, resultModel.Name); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenRoleIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateRoleWebModel updateRoleWebModel = new() + { + Name = NAME + }; + UpdateRoleServiceModel updateRoleServiceModel = new() + { + Name = NAME + }; + + this._roleServiceMock + .Setup(p => p.UpdateRole(It.IsAny<UpdateRoleServiceModel>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<UpdateRoleServiceModel>(It.IsAny<UpdateRoleWebModel>())) + .Returns(updateRoleServiceModel); + + IActionResult result = this._roleController.Update(id, updateRoleWebModel).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Update_ShouldReturnBadObjectResult_WhenRoleIsNotUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + string message = "Could not update role!"; + UpdateRoleWebModel updateRoleWebModel = new() + { + Name = NAME + }; + UpdateRoleServiceModel updateRoleServiceModel = new() + { + Name = NAME + }; + + this._roleServiceMock + .Setup(p => p.UpdateRole(It.IsAny<UpdateRoleServiceModel>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<UpdateRoleServiceModel>(It.IsAny<UpdateRoleWebModel>())) + .Returns(updateRoleServiceModel); + + IActionResult result = this._roleController.Update(id, updateRoleWebModel).Result; + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenRoleIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._roleServiceMock + .Setup(p => p.DeleteRole(It.IsAny<Guid>())) + .ReturnsAsync(true); + + IActionResult result = this._roleController.Delete(id).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delete_ReturnsBadRequestObjectResult_WhenRoleIsNotDeletedSuccessfully() + { + string message = "Could not delete role!"; + Guid id = Guid.NewGuid(); + + this._roleServiceMock + .Setup(p => p.DeleteRole(It.IsAny<Guid>())) + .ReturnsAsync(false); + + IActionResult result = this._roleController.Delete(id).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + } +} diff --git a/src/DevHive.Tests/DevHive.Web.Tests/TechnologyController.Tests.cs b/src/Web/DevHive.Web.Tests/TechnologyController.Tests.cs index 164bcbf..723fb73 100644 --- a/src/DevHive.Tests/DevHive.Web.Tests/TechnologyController.Tests.cs +++ b/src/Web/DevHive.Web.Tests/TechnologyController.Tests.cs @@ -1,5 +1,4 @@ using AutoMapper; -using DevHive.Common.Models.Misc; using DevHive.Services.Interfaces; using DevHive.Services.Models.Technology; using DevHive.Web.Controllers; @@ -9,7 +8,6 @@ using Moq; using NUnit.Framework; using System; using System.Linq; -using System.Threading.Tasks; namespace DevHive.Web.Tests { @@ -17,17 +15,17 @@ namespace DevHive.Web.Tests public class TechnologyControllerTests { const string NAME = "Gosho Trapov"; - private Mock<ITechnologyService> TechnologyServiceMock { get; set; } - private Mock<IMapper> MapperMock { get; set; } - private TechnologyController TechnologyController { get; set; } + private Mock<ITechnologyService> _technologyServiceMock; + private Mock<IMapper> _mapperMock; + private TechnologyController _technologyController; #region SetUp [SetUp] public void SetUp() { - this.TechnologyServiceMock = new Mock<ITechnologyService>(); - this.MapperMock = new Mock<IMapper>(); - this.TechnologyController = new TechnologyController(this.TechnologyServiceMock.Object, this.MapperMock.Object); + this._technologyServiceMock = new Mock<ITechnologyService>(); + this._mapperMock = new Mock<IMapper>(); + this._technologyController = new TechnologyController(this._technologyServiceMock.Object, this._mapperMock.Object); } #endregion @@ -35,20 +33,24 @@ namespace DevHive.Web.Tests [Test] public void Create_ReturnsOkObjectResult_WhenTechnologyIsSuccessfullyCreated() { - CreateTechnologyWebModel createTechnologyWebModel = new CreateTechnologyWebModel + CreateTechnologyWebModel createTechnologyWebModel = new() { Name = NAME }; - CreateTechnologyServiceModel createTechnologyServiceModel = new CreateTechnologyServiceModel + CreateTechnologyServiceModel createTechnologyServiceModel = new() { Name = NAME }; Guid id = Guid.NewGuid(); - this.MapperMock.Setup(p => p.Map<CreateTechnologyServiceModel>(It.IsAny<CreateTechnologyWebModel>())).Returns(createTechnologyServiceModel); - this.TechnologyServiceMock.Setup(p => p.CreateTechnology(It.IsAny<CreateTechnologyServiceModel>())).Returns(Task.FromResult(id)); + this._mapperMock + .Setup(p => p.Map<CreateTechnologyServiceModel>(It.IsAny<CreateTechnologyWebModel>())) + .Returns(createTechnologyServiceModel); + this._technologyServiceMock + .Setup(p => p.CreateTechnology(It.IsAny<CreateTechnologyServiceModel>())) + .ReturnsAsync(id); - IActionResult result = this.TechnologyController.Create(createTechnologyWebModel).Result; + IActionResult result = this._technologyController.Create(createTechnologyWebModel).Result; Assert.IsInstanceOf<OkObjectResult>(result); @@ -66,26 +68,30 @@ namespace DevHive.Web.Tests [Test] public void Create_ReturnsBadRequestObjectResult_WhenTechnologyIsNotCreatedSuccessfully() { - CreateTechnologyWebModel createTechnologyWebModel = new CreateTechnologyWebModel + CreateTechnologyWebModel createTechnologyWebModel = new() { Name = NAME }; - CreateTechnologyServiceModel createTechnologyServiceModel = new CreateTechnologyServiceModel + CreateTechnologyServiceModel createTechnologyServiceModel = new() { Name = NAME }; Guid id = Guid.Empty; string errorMessage = $"Could not create technology {NAME}"; - this.MapperMock.Setup(p => p.Map<CreateTechnologyServiceModel>(It.IsAny<CreateTechnologyWebModel>())).Returns(createTechnologyServiceModel); - this.TechnologyServiceMock.Setup(p => p.CreateTechnology(It.IsAny<CreateTechnologyServiceModel>())).Returns(Task.FromResult(id)); + this._mapperMock + .Setup(p => p.Map<CreateTechnologyServiceModel>(It.IsAny<CreateTechnologyWebModel>())) + .Returns(createTechnologyServiceModel); + this._technologyServiceMock + .Setup(p => p.CreateTechnology(It.IsAny<CreateTechnologyServiceModel>())) + .ReturnsAsync(id); - IActionResult result = this.TechnologyController.Create(createTechnologyWebModel).Result; + IActionResult result = this._technologyController.Create(createTechnologyWebModel).Result; Assert.IsInstanceOf<BadRequestObjectResult>(result); - BadRequestObjectResult badRequsetObjectResult = result as BadRequestObjectResult; - string resultMessage = badRequsetObjectResult.Value.ToString(); + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultMessage = badRequestObjectResult.Value.ToString(); Assert.AreEqual(errorMessage, resultMessage); } @@ -97,19 +103,23 @@ namespace DevHive.Web.Tests { Guid id = Guid.NewGuid(); - ReadTechnologyWebModel readTechnologyWebModel = new ReadTechnologyWebModel + ReadTechnologyWebModel readTechnologyWebModel = new() { Name = NAME }; - ReadTechnologyServiceModel readTechnologyServiceModel = new ReadTechnologyServiceModel + ReadTechnologyServiceModel readTechnologyServiceModel = new() { Name = NAME }; - this.TechnologyServiceMock.Setup(p => p.GetTechnologyById(It.IsAny<Guid>())).Returns(Task.FromResult(readTechnologyServiceModel)); - this.MapperMock.Setup(p => p.Map<ReadTechnologyWebModel>(It.IsAny<ReadTechnologyServiceModel>())).Returns(readTechnologyWebModel); + this._technologyServiceMock + .Setup(p => p.GetTechnologyById(It.IsAny<Guid>())) + .ReturnsAsync(readTechnologyServiceModel); + this._mapperMock + .Setup(p => p.Map<ReadTechnologyWebModel>(It.IsAny<ReadTechnologyServiceModel>())) + .Returns(readTechnologyWebModel); - IActionResult result = this.TechnologyController.GetById(id).Result; + IActionResult result = this._technologyController.GetById(id).Result; Assert.IsInstanceOf<OkObjectResult>(result); @@ -125,19 +135,23 @@ namespace DevHive.Web.Tests public void Update_ShouldReturnOkResult_WhenTechnologyIsUpdatedSuccessfully() { Guid id = Guid.NewGuid(); - UpdateTechnologyWebModel updateTechnologyWebModel = new UpdateTechnologyWebModel + UpdateTechnologyWebModel updateTechnologyWebModel = new() { Name = NAME }; - UpdateTechnologyServiceModel updateTechnologyServiceModel = new UpdateTechnologyServiceModel + UpdateTechnologyServiceModel updateTechnologyServiceModel = new() { Name = NAME }; - this.TechnologyServiceMock.Setup(p => p.UpdateTechnology(It.IsAny<UpdateTechnologyServiceModel>())).Returns(Task.FromResult(true)); - this.MapperMock.Setup(p => p.Map<UpdateTechnologyServiceModel>(It.IsAny<UpdateTechnologyWebModel>())).Returns(updateTechnologyServiceModel); + this._technologyServiceMock + .Setup(p => p.UpdateTechnology(It.IsAny<UpdateTechnologyServiceModel>())) + .ReturnsAsync(true); + this._mapperMock + .Setup(p => p.Map<UpdateTechnologyServiceModel>(It.IsAny<UpdateTechnologyWebModel>())) + .Returns(updateTechnologyServiceModel); - IActionResult result = this.TechnologyController.Update(id, updateTechnologyWebModel).Result; + IActionResult result = this._technologyController.Update(id, updateTechnologyWebModel).Result; Assert.IsInstanceOf<OkResult>(result); } @@ -147,19 +161,23 @@ namespace DevHive.Web.Tests { Guid id = Guid.NewGuid(); string message = "Could not update Technology"; - UpdateTechnologyWebModel updateTechnologyWebModel = new UpdateTechnologyWebModel + UpdateTechnologyWebModel updateTechnologyWebModel = new() { Name = NAME }; - UpdateTechnologyServiceModel updateTechnologyServiceModel = new UpdateTechnologyServiceModel + UpdateTechnologyServiceModel updateTechnologyServiceModel = new() { Name = NAME }; - this.TechnologyServiceMock.Setup(p => p.UpdateTechnology(It.IsAny<UpdateTechnologyServiceModel>())).Returns(Task.FromResult(false)); - this.MapperMock.Setup(p => p.Map<UpdateTechnologyServiceModel>(It.IsAny<UpdateTechnologyWebModel>())).Returns(updateTechnologyServiceModel); + this._technologyServiceMock + .Setup(p => p.UpdateTechnology(It.IsAny<UpdateTechnologyServiceModel>())) + .ReturnsAsync(false); + this._mapperMock + .Setup(p => p.Map<UpdateTechnologyServiceModel>(It.IsAny<UpdateTechnologyWebModel>())) + .Returns(updateTechnologyServiceModel); - IActionResult result = this.TechnologyController.Update(id, updateTechnologyWebModel).Result; + IActionResult result = this._technologyController.Update(id, updateTechnologyWebModel).Result; Assert.IsInstanceOf<BadRequestObjectResult>(result); BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; @@ -175,9 +193,11 @@ namespace DevHive.Web.Tests { Guid id = Guid.NewGuid(); - this.TechnologyServiceMock.Setup(p => p.DeleteTechnology(It.IsAny<Guid>())).Returns(Task.FromResult(true)); + this._technologyServiceMock + .Setup(p => p.DeleteTechnology(It.IsAny<Guid>())) + .ReturnsAsync(true); - IActionResult result = this.TechnologyController.Delete(id).Result; + IActionResult result = this._technologyController.Delete(id).Result; Assert.IsInstanceOf<OkResult>(result); } @@ -188,9 +208,11 @@ namespace DevHive.Web.Tests string message = "Could not delete Technology"; Guid id = Guid.NewGuid(); - this.TechnologyServiceMock.Setup(p => p.DeleteTechnology(It.IsAny<Guid>())).Returns(Task.FromResult(false)); + this._technologyServiceMock + .Setup(p => p.DeleteTechnology(It.IsAny<Guid>())) + .ReturnsAsync(false); - IActionResult result = this.TechnologyController.Delete(id).Result; + IActionResult result = this._technologyController.Delete(id).Result; Assert.IsInstanceOf<BadRequestObjectResult>(result); diff --git a/src/Web/DevHive.Web.Tests/UserController.Tests.cs b/src/Web/DevHive.Web.Tests/UserController.Tests.cs new file mode 100644 index 0000000..3f88709 --- /dev/null +++ b/src/Web/DevHive.Web.Tests/UserController.Tests.cs @@ -0,0 +1,257 @@ +using System; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Common.Models.Identity; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.User; +using DevHive.Web.Controllers; +using DevHive.Web.Models.User; +using Microsoft.AspNetCore.Mvc; +using Moq; +using NUnit.Framework; + +namespace DevHive.Web.Tests +{ + [TestFixture] + public class UserControllerTests + { + const string USERNAME = "Gosho Trapov"; + private Mock<IUserService> _userServiceMock; + private Mock<IMapper> _mapperMock; + private Mock<IJwtService> _jwtServiceMock; + private UserController _userController; + + [SetUp] + public void SetUp() + { + this._userServiceMock = new Mock<IUserService>(); + this._mapperMock = new Mock<IMapper>(); + this._jwtServiceMock = new Mock<IJwtService>(); + this._userController = new UserController(this._userServiceMock.Object, this._mapperMock.Object, this._jwtServiceMock.Object); + } + + #region Create + [Test] + public void LoginUser_ReturnsOkObjectResult_WhenUserIsSuccessfullyLoggedIn() + { + LoginWebModel loginWebModel = new() + { + UserName = USERNAME + }; + LoginServiceModel loginServiceModel = new() + { + UserName = USERNAME + }; + string token = "goshotrapov"; + TokenModel tokenModel = new(token); + TokenWebModel tokenWebModel = new(token); + + this._mapperMock + .Setup(p => p.Map<LoginServiceModel>(It.IsAny<LoginWebModel>())) + .Returns(loginServiceModel); + this._mapperMock + .Setup(p => p.Map<TokenWebModel>(It.IsAny<TokenModel>())) + .Returns(tokenWebModel); + this._userServiceMock + .Setup(p => p.LoginUser(It.IsAny<LoginServiceModel>())) + .ReturnsAsync(tokenModel); + + IActionResult result = this._userController.Login(loginWebModel).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + var resultToken = ((result as OkObjectResult).Value as TokenWebModel).Token; + + Assert.AreEqual(token, resultToken); + } + + [Test] + public void RegisterUser_ReturnsOkObjectResult_WhenUserIsSuccessfullyRegistered() + { + RegisterWebModel registerWebModel = new() + { + UserName = USERNAME + }; + RegisterServiceModel registerServiceModel = new() + { + UserName = USERNAME + }; + string token = "goshotrapov"; + TokenModel tokenModel = new(token); + TokenWebModel tokenWebModel = new(token); + + this._mapperMock + .Setup(p => p.Map<RegisterServiceModel>(It.IsAny<RegisterWebModel>())) + .Returns(registerServiceModel); + this._mapperMock + .Setup(p => p.Map<TokenWebModel>(It.IsAny<TokenModel>())) + .Returns(tokenWebModel); + this._userServiceMock + .Setup(p => p.RegisterUser(It.IsAny<RegisterServiceModel>())) + .ReturnsAsync(tokenModel); + + IActionResult result = this._userController.Register(registerWebModel).Result; + + Assert.IsInstanceOf<CreatedResult>(result); + + CreatedResult createdResult = result as CreatedResult; + TokenWebModel resultModel = (createdResult.Value as TokenWebModel); + + Assert.AreEqual(token, resultModel.Token); + } + #endregion + + #region Read + [Test] + public void GetById_ReturnsTheUser_WhenItExists() + { + Guid id = Guid.NewGuid(); + + UserServiceModel userServiceModel = new() + { + UserName = USERNAME + }; + UserWebModel userWebModel = new() + { + UserName = USERNAME + }; + + this._userServiceMock + .Setup(p => p.GetUserById(It.IsAny<Guid>())) + .ReturnsAsync(userServiceModel); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<UserWebModel>(It.IsAny<UserServiceModel>())) + .Returns(userWebModel); + + IActionResult result = this._userController.GetById(id, null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + UserWebModel resultModel = okObjectResult.Value as UserWebModel; + + Assert.AreEqual(USERNAME, resultModel.UserName); + } + + [Test] + public void GetById_ReturnsUnauthorizedResult_WhenUserIsNotAuthorized() + { + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(false); + + IActionResult result = this._userController.GetById(Guid.NewGuid(), null).Result; + + Assert.IsInstanceOf<UnauthorizedResult>(result); + } + + [Test] + public void GetUser_ReturnsTheUser_WhenItExists() + { + UserWebModel userWebModel = new() + { + UserName = USERNAME + }; + UserServiceModel userServiceModel = new() + { + UserName = USERNAME + }; + + this._userServiceMock + .Setup(p => p.GetUserByUsername(It.IsAny<string>())) + .ReturnsAsync(userServiceModel); + this._mapperMock + .Setup(p => p.Map<UserWebModel>(It.IsAny<UserServiceModel>())) + .Returns(userWebModel); + + IActionResult result = this._userController.GetUser(null).Result; + + Assert.IsInstanceOf<OkObjectResult>(result); + + OkObjectResult okObjectResult = result as OkObjectResult; + UserWebModel resultModel = okObjectResult.Value as UserWebModel; + + Assert.AreEqual(USERNAME, resultModel.UserName); + } + #endregion + + #region Update + [Test] + public void Update_ShouldReturnOkResult_WhenUserIsUpdatedSuccessfully() + { + Guid id = Guid.NewGuid(); + UpdateUserWebModel updateUserWebModel = new() + { + UserName = USERNAME + }; + UpdateUserServiceModel updateUserServiceModel = new() + { + UserName = USERNAME + }; + UserServiceModel userServiceModel = new() + { + UserName = USERNAME + }; + + this._userServiceMock + .Setup(p => p.UpdateUser(It.IsAny<UpdateUserServiceModel>())) + .ReturnsAsync(userServiceModel); + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._mapperMock + .Setup(p => p.Map<UpdateUserServiceModel>(It.IsAny<UpdateUserWebModel>())) + .Returns(updateUserServiceModel); + + IActionResult result = this._userController.Update(id, updateUserWebModel, null).Result; + + Assert.IsInstanceOf<AcceptedResult>(result); + } + #endregion + + #region Delete + [Test] + public void Delete_ReturnsOkResult_WhenUserIsDeletedSuccessfully() + { + Guid id = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._userServiceMock + .Setup(p => p.DeleteUser(It.IsAny<Guid>())) + .ReturnsAsync(true); + + IActionResult result = this._userController.Delete(id, null).Result; + + Assert.IsInstanceOf<OkResult>(result); + } + + [Test] + public void Delete_ReturnsBadRequestObjectResult_WhenUserIsNotDeletedSuccessfully() + { + string message = "Could not delete User"; + Guid id = Guid.NewGuid(); + + this._jwtServiceMock + .Setup(p => p.ValidateToken(It.IsAny<Guid>(), It.IsAny<string>())) + .Returns(true); + this._userServiceMock + .Setup(p => p.DeleteUser(It.IsAny<Guid>())) + .ReturnsAsync(false); + + IActionResult result = this._userController.Delete(id, null).Result; + + Assert.IsInstanceOf<BadRequestObjectResult>(result); + + BadRequestObjectResult badRequestObjectResult = result as BadRequestObjectResult; + string resultModel = badRequestObjectResult.Value.ToString(); + + Assert.AreEqual(message, resultModel); + } + #endregion + } +} diff --git a/src/DevHive.Web/Configurations/Extensions/ConfigureAutoMapper.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureAutoMapper.cs index 8b7d657..cd5679f 100644 --- a/src/DevHive.Web/Configurations/Extensions/ConfigureAutoMapper.cs +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureAutoMapper.cs @@ -1,6 +1,8 @@ using System; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; using AutoMapper; -//using AutoMapper.Configuration; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; @@ -15,10 +17,10 @@ namespace DevHive.Web.Configurations.Extensions public static void UseAutoMapperConfiguration(this IApplicationBuilder app) { - var config = new MapperConfiguration(cfg => + _ = new MapperConfiguration(cfg => { cfg.AllowNullCollections = true; }); } } -}
\ No newline at end of file +} diff --git a/src/DevHive.Web/Configurations/Extensions/ConfigureDatabase.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDatabase.cs index 9f02dd7..b4c49b4 100644 --- a/src/DevHive.Web/Configurations/Extensions/ConfigureDatabase.cs +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDatabase.cs @@ -7,6 +7,8 @@ using Microsoft.AspNetCore.Builder; using System; using Microsoft.AspNetCore.Authentication.JwtBearer; using DevHive.Data; +using System.Linq; +using System.Threading.Tasks; namespace DevHive.Web.Configurations.Extensions { @@ -16,8 +18,11 @@ namespace DevHive.Web.Configurations.Extensions { services.AddDbContext<DevHiveContext>(options => { - options.EnableSensitiveDataLogging(true); - options.UseNpgsql(configuration.GetConnectionString("DEV")); + // options.EnableSensitiveDataLogging(true); + options.UseNpgsql(configuration.GetConnectionString("DEV"), options => + { + options.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery); + }); }); services.AddIdentity<User, Role>() @@ -65,6 +70,29 @@ namespace DevHive.Web.Configurations.Extensions app.UseAuthentication(); app.UseAuthorization(); + + using var serviceScope = app.ApplicationServices.CreateScope(); + using var dbContext = serviceScope.ServiceProvider.GetRequiredService<DevHiveContext>(); + + dbContext.Database.Migrate(); + + var roleManager = (RoleManager<Role>)serviceScope.ServiceProvider.GetService(typeof(RoleManager<Role>)); + + if (!dbContext.Roles.Any(x => x.Name == Role.DefaultRole)) + { + Role defaultRole = new() { Name = Role.DefaultRole }; + + roleManager.CreateAsync(defaultRole).Wait(); + } + + if (!dbContext.Roles.Any(x => x.Name == Role.AdminRole)) + { + Role adminRole = new() { Name = Role.AdminRole }; + + roleManager.CreateAsync(adminRole).Wait(); + } + + dbContext.SaveChanges(); } } } diff --git a/src/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs index 88f21d4..f49a335 100644 --- a/src/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureDependencyInjection.cs @@ -1,4 +1,7 @@ -using DevHive.Data.Interfaces.Repositories; +using System.Text; +using DevHive.Common.Jwt; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Data.Interfaces; using DevHive.Data.Repositories; using DevHive.Services.Interfaces; using DevHive.Services.Services; @@ -14,25 +17,35 @@ namespace DevHive.Web.Configurations.Extensions services.AddTransient<ILanguageRepository, LanguageRepository>(); services.AddTransient<IRoleRepository, RoleRepository>(); services.AddTransient<ITechnologyRepository, TechnologyRepository>(); - services.AddTransient<IUserRepository, UserRepository>(); services.AddTransient<IPostRepository, PostRepository>(); services.AddTransient<ICommentRepository, CommentRepository>(); services.AddTransient<IFeedRepository, FeedRepository>(); services.AddTransient<IRatingRepository, RatingRepository>(); + services.AddTransient<IProfilePictureRepository, ProfilePictureRepository>(); + services.AddTransient<IUserRepository, UserRepository>(); services.AddTransient<ILanguageService, LanguageService>(); services.AddTransient<IRoleService, RoleService>(); services.AddTransient<ITechnologyService, TechnologyService>(); - services.AddTransient<IUserService, UserService>(); services.AddTransient<IPostService, PostService>(); services.AddTransient<ICommentService, CommentService>(); services.AddTransient<IFeedService, FeedService>(); + services.AddTransient<IRatingService, RatingService>(); + services.AddTransient<IProfilePictureService, ProfilePictureService>(); + services.AddTransient<IUserService, UserService>(); + services.AddTransient<IFriendsService, FriendsService>(); + services.AddTransient<ICloudService, CloudinaryService>(options => new CloudinaryService( cloudName: configuration.GetSection("Cloud").GetSection("cloudName").Value, apiKey: configuration.GetSection("Cloud").GetSection("apiKey").Value, apiSecret: configuration.GetSection("Cloud").GetSection("apiSecret").Value)); - services.AddTransient<IRateService, RateService>(); + + services.AddSingleton<IJwtService, JwtService>(options => + new JwtService( + signingKey: Encoding.ASCII.GetBytes(configuration.GetSection("Jwt").GetSection("signingKey").Value), + validationIssuer: configuration.GetSection("Jwt").GetSection("validationIssuer").Value, + audience: configuration.GetSection("Jwt").GetSection("audience").Value)); } } } diff --git a/src/Web/DevHive.Web/Configurations/Extensions/ConfigureExceptionHandlerMiddleware.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureExceptionHandlerMiddleware.cs new file mode 100644 index 0000000..6885f84 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureExceptionHandlerMiddleware.cs @@ -0,0 +1,36 @@ +using DevHive.Web.Middleware; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; + +namespace DevHive.Web.Configurations.Extensions +{ + public static class ConfigureExceptionHandlerMiddleware + { + public static void ConfigureExceptionHandler(this IServiceCollection services, IConfiguration configuration) + { + services.Configure<ApiBehaviorOptions>(o => + { + o.InvalidModelStateResponseFactory = actionContext => + { + var problemDetails = new ValidationProblemDetails(actionContext.ModelState) + { + Status = StatusCodes.Status422UnprocessableEntity + }; + + return new UnprocessableEntityObjectResult(problemDetails) + { + ContentTypes = { "application/problem+json" } + }; + }; + }); + } + + public static void UseExceptionHandlerMiddlewareConfiguration(this IApplicationBuilder app) + { + app.UseMiddleware<ExceptionMiddleware>(); + } + } +} diff --git a/src/DevHive.Web/Configurations/Extensions/ConfigureJWT.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureJwt.cs index d422bc8..18127bc 100644 --- a/src/DevHive.Web/Configurations/Extensions/ConfigureJWT.cs +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureJwt.cs @@ -1,6 +1,5 @@ using System.Text; using System.Threading.Tasks; -using DevHive.Services.Options; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -8,19 +7,14 @@ using Microsoft.IdentityModel.Tokens; namespace DevHive.Web.Configurations.Extensions { - public static class JWTExtensions + public static class ConfigureJwt { public static void JWTConfiguration(this IServiceCollection services, IConfiguration configuration) { - services.AddSingleton(new JWTOptions(configuration - .GetSection("AppSettings") - .GetSection("Secret") - .Value)); - // Get key from appsettings.json - var key = Encoding.ASCII.GetBytes(configuration - .GetSection("AppSettings") - .GetSection("Secret") + var signingKey = Encoding.ASCII.GetBytes(configuration + .GetSection("Jwt") + .GetSection("signingKey") .Value); // Setup Jwt Authentication @@ -35,7 +29,6 @@ namespace DevHive.Web.Configurations.Extensions { OnTokenValidated = context => { - // TODO: add more authentication return Task.CompletedTask; } }; @@ -43,12 +36,11 @@ namespace DevHive.Web.Configurations.Extensions x.SaveToken = true; x.TokenValidationParameters = new TokenValidationParameters { - //ValidateIssuerSigningKey = false, - IssuerSigningKey = new SymmetricSecurityKey(key), + IssuerSigningKey = new SymmetricSecurityKey(signingKey), ValidateIssuer = false, ValidateAudience = false }; }); } } -}
\ No newline at end of file +} diff --git a/src/Web/DevHive.Web/Configurations/Extensions/ConfigureSwagger.cs b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureSwagger.cs new file mode 100644 index 0000000..1b7182c --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Extensions/ConfigureSwagger.cs @@ -0,0 +1,65 @@ +using System.Linq; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using NSwag; +using NSwag.Generation.Processors.Security; + +namespace DevHive.Web.Configurations.Extensions +{ + public static class SwaggerExtensions + { +#pragma warning disable S1075 + private const string LicenseName = "GPL-3.0 License"; + private const string LicenseUri = "https://github.com/Team-Kaleidoscope/DevHive/blob/main/LICENSE"; + private const string TermsOfServiceUri = "https://example.com/terms"; +#pragma warning restore S1075 + + public static void SwaggerConfiguration(this IServiceCollection services) + { + services.AddOpenApiDocument(c => + { + c.GenerateXmlObjects = true; + c.UseControllerSummaryAsTagDescription = true; + + c.AllowNullableBodyParameters = false; + c.Description = "DevHive Social Media's API Endpoints"; + + c.PostProcess = doc => + { + doc.Info.Version = "v0.1"; + doc.Info.Title = "API"; + doc.Info.Description = "DevHive Social Media's first official API release"; + doc.Info.TermsOfService = TermsOfServiceUri; + doc.Info.License = new() + { + Name = LicenseName, + Url = LicenseUri + }; + }; + + c.AddSecurity("Bearer", Enumerable.Empty<string>(), new() + { + Type = OpenApiSecuritySchemeType.ApiKey, + Name = "Authorization", + In = OpenApiSecurityApiKeyLocation.Header, + Description = "Type into the textbox: Bearer {your JWT token}." + }); + c.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("Bearer")); + }); + } + + public static void UseSwaggerConfiguration(this IApplicationBuilder app) + { + app.UseOpenApi(c => + { + c.DocumentName = "v0.1"; + }); + app.UseSwaggerUi3(c => + { + c.DocumentTitle = "DevHive API"; + c.EnableTryItOut = false; + c.DocExpansion = "list"; + }); + } + } +} diff --git a/src/DevHive.Web/Configurations/Mapping/CommentMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/CommentMappings.cs index b8d6829..b8d6829 100644 --- a/src/DevHive.Web/Configurations/Mapping/CommentMappings.cs +++ b/src/Web/DevHive.Web/Configurations/Mapping/CommentMappings.cs diff --git a/src/DevHive.Web/Configurations/Mapping/FeedMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/FeedMappings.cs index 0909f6d..0909f6d 100644 --- a/src/DevHive.Web/Configurations/Mapping/FeedMappings.cs +++ b/src/Web/DevHive.Web/Configurations/Mapping/FeedMappings.cs diff --git a/src/DevHive.Web/Configurations/Mapping/LanguageMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/LanguageMappings.cs index eca0d1a..eca0d1a 100644 --- a/src/DevHive.Web/Configurations/Mapping/LanguageMappings.cs +++ b/src/Web/DevHive.Web/Configurations/Mapping/LanguageMappings.cs diff --git a/src/DevHive.Web/Configurations/Mapping/PostMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/PostMappings.cs index a5b46ee..a5b46ee 100644 --- a/src/DevHive.Web/Configurations/Mapping/PostMappings.cs +++ b/src/Web/DevHive.Web/Configurations/Mapping/PostMappings.cs diff --git a/src/Web/DevHive.Web/Configurations/Mapping/ProfilePictureMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/ProfilePictureMappings.cs new file mode 100644 index 0000000..8c12a20 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/ProfilePictureMappings.cs @@ -0,0 +1,16 @@ +using System; +using AutoMapper; +using DevHive.Web.Models.ProfilePicture; +using DevHive.Services.Models.ProfilePicture; + +namespace DevHive.Web.Configurations.Mapping +{ + public class ProfilePictureMappings : Profile + { + public ProfilePictureMappings() + { + CreateMap<ProfilePictureWebModel, ProfilePictureServiceModel>() + .ForMember(dest => dest.ProfilePictureFormFile, src => src.MapFrom(p => p.Picture)); + } + } +} diff --git a/src/Web/DevHive.Web/Configurations/Mapping/RatingMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/RatingMappings.cs new file mode 100644 index 0000000..1d731d8 --- /dev/null +++ b/src/Web/DevHive.Web/Configurations/Mapping/RatingMappings.cs @@ -0,0 +1,18 @@ +using AutoMapper; +using DevHive.Services.Models.Rating; +using DevHive.Web.Models.Rating; + +namespace DevHive.Web.Configurations.Mapping +{ + public class RatingMappings : Profile + { + public RatingMappings() + { + CreateMap<CreateRatingWebModel, CreateRatingServiceModel>(); + + CreateMap<ReadRatingServiceModel, ReadRatingWebModel>(); + + CreateMap<UpdateRatingWebModel, UpdateRatingServiceModel>(); + } + } +} diff --git a/src/DevHive.Web/Configurations/Mapping/RoleMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/RoleMappings.cs index 2ea2742..60f8503 100644 --- a/src/DevHive.Web/Configurations/Mapping/RoleMappings.cs +++ b/src/Web/DevHive.Web/Configurations/Mapping/RoleMappings.cs @@ -1,6 +1,6 @@ using AutoMapper; -using DevHive.Web.Models.Identity.Role; -using DevHive.Services.Models.Identity.Role; +using DevHive.Web.Models.Role; +using DevHive.Services.Models.Role; namespace DevHive.Web.Configurations.Mapping { diff --git a/src/DevHive.Web/Configurations/Mapping/TechnologyMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/TechnologyMappings.cs index 708b6ac..708b6ac 100644 --- a/src/DevHive.Web/Configurations/Mapping/TechnologyMappings.cs +++ b/src/Web/DevHive.Web/Configurations/Mapping/TechnologyMappings.cs diff --git a/src/DevHive.Web/Configurations/Mapping/UserMappings.cs b/src/Web/DevHive.Web/Configurations/Mapping/UserMappings.cs index f58e7ca..dabec93 100644 --- a/src/DevHive.Web/Configurations/Mapping/UserMappings.cs +++ b/src/Web/DevHive.Web/Configurations/Mapping/UserMappings.cs @@ -1,6 +1,6 @@ using AutoMapper; -using DevHive.Services.Models.Identity.User; -using DevHive.Web.Models.Identity.User; +using DevHive.Services.Models.User; +using DevHive.Web.Models.User; using DevHive.Common.Models.Identity; namespace DevHive.Web.Configurations.Mapping @@ -23,9 +23,6 @@ namespace DevHive.Web.Configurations.Mapping CreateMap<UsernameWebModel, FriendServiceModel>(); CreateMap<UsernameWebModel, UpdateFriendServiceModel>(); - CreateMap<UpdateProfilePictureWebModel, UpdateProfilePictureServiceModel>(); - CreateMap<ProfilePictureServiceModel, ProfilePictureWebModel>(); - CreateMap<UpdateUserServiceModel, UpdateUserWebModel>(); CreateMap<FriendServiceModel, UsernameWebModel>(); } diff --git a/src/DevHive.Web/Controllers/CommentController.cs b/src/Web/DevHive.Web/Controllers/CommentController.cs index c38e300..8fa3577 100644 --- a/src/DevHive.Web/Controllers/CommentController.cs +++ b/src/Web/DevHive.Web/Controllers/CommentController.cs @@ -6,9 +6,13 @@ using DevHive.Web.Models.Comment; using DevHive.Services.Models.Comment; using Microsoft.AspNetCore.Authorization; using DevHive.Services.Interfaces; +using DevHive.Common.Jwt.Interfaces; namespace DevHive.Web.Controllers { + /// <summary> + /// All endpoints for interacting with the comments layer + /// </summary> [ApiController] [Route("/api/[controller]")] [Authorize(Roles = "User,Admin")] @@ -16,16 +20,28 @@ namespace DevHive.Web.Controllers { private readonly ICommentService _commentService; private readonly IMapper _commentMapper; + private readonly IJwtService _jwtService; - public CommentController(ICommentService commentService, IMapper commentMapper) + public CommentController(ICommentService commentService, IMapper commentMapper, IJwtService jwtService) { this._commentService = commentService; this._commentMapper = commentMapper; + this._jwtService = jwtService; } + /// <summary> + /// Create a comment and attach it to a post + /// </summary> + /// <param name="userId">The useer's Id</param> + /// <param name="createCommentWebModel">The new comment's parametars</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>The comment's Id</returns> [HttpPost] public async Task<IActionResult> AddComment(Guid userId, [FromBody] CreateCommentWebModel createCommentWebModel, [FromHeader] string authorization) { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + if (!await this._commentService.ValidateJwtForCreating(userId, authorization)) return new UnauthorizedResult(); @@ -40,20 +56,32 @@ namespace DevHive.Web.Controllers new OkObjectResult(new { Id = id }); } + /// <summary> + /// Query comment's data by it's Id + /// </summary> + /// <param name="commentId">The comment's Id</param> + /// <returns>Full data model of the comment</returns> [HttpGet] [AllowAnonymous] - public async Task<IActionResult> GetCommentById(Guid id) + public async Task<IActionResult> GetCommentById(Guid commentId) { - ReadCommentServiceModel readCommentServiceModel = await this._commentService.GetCommentById(id); + ReadCommentServiceModel readCommentServiceModel = await this._commentService.GetCommentById(commentId); ReadCommentWebModel readCommentWebModel = this._commentMapper.Map<ReadCommentWebModel>(readCommentServiceModel); return new OkObjectResult(readCommentWebModel); } + /// <summary> + /// Update comment's parametars. Comment creator only! + /// </summary> + /// <param name="userId">The comment creator's Id</param> + /// <param name="updateCommentWebModel">New comment's parametars</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>Ok result</returns> [HttpPut] public async Task<IActionResult> UpdateComment(Guid userId, [FromBody] UpdateCommentWebModel updateCommentWebModel, [FromHeader] string authorization) { - if (!await this._commentService.ValidateJwtForComment(updateCommentWebModel.CommentId, authorization)) + if (!this._jwtService.ValidateToken(userId, authorization)) return new UnauthorizedResult(); UpdateCommentServiceModel updateCommentServiceModel = @@ -67,17 +95,22 @@ namespace DevHive.Web.Controllers new OkObjectResult(new { Id = id }); } + /// <summary> + /// Delete a comment. Comment creator only! + /// </summary> + /// <param name="commentId">Comment's Id</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>Ok result</returns> [HttpDelete] - public async Task<IActionResult> DeleteComment(Guid id, [FromHeader] string authorization) + public async Task<IActionResult> DeleteComment(Guid commentId, [FromHeader] string authorization) { - if (!await this._commentService.ValidateJwtForComment(id, authorization)) + if (!await this._commentService.ValidateJwtForComment(commentId, authorization)) return new UnauthorizedResult(); - return await this._commentService.DeleteComment(id) ? + return await this._commentService.DeleteComment(commentId) ? new OkResult() : new BadRequestObjectResult("Could not delete Comment"); } - } } diff --git a/src/DevHive.Web/Controllers/FeedController.cs b/src/Web/DevHive.Web/Controllers/FeedController.cs index abca3e4..37532a9 100644 --- a/src/DevHive.Web/Controllers/FeedController.cs +++ b/src/Web/DevHive.Web/Controllers/FeedController.cs @@ -10,6 +10,9 @@ using Microsoft.AspNetCore.Mvc; namespace DevHive.Web.Controllers { + /// <summary> + /// All endpoints for interacting with the feed layer + /// </summary> [ApiController] [Route("/api/[controller]")] [Authorize(Roles = "User,Admin")] @@ -24,6 +27,12 @@ namespace DevHive.Web.Controllers this._mapper = mapper; } + /// <summary> + /// Query posts for user's feed + /// </summary> + /// <param name="userId">The user's Id, whose feed is begin queried</param> + /// <param name="getPageWebModel">Page parametars</param> + /// <returns>A page of the feed</returns> [HttpPost] [Route("GetPosts")] public async Task<IActionResult> GetPosts(Guid userId, [FromBody] GetPageWebModel getPageWebModel) @@ -37,6 +46,12 @@ namespace DevHive.Web.Controllers return new OkObjectResult(readPageWebModel); } + /// <summary> + /// Query a user profile's posts + /// </summary> + /// <param name="username">The user's username, whose posts are being queried</param> + /// <param name="getPageWebModel">Page parametars</param> + /// <returns>A page of the user's posts</returns> [HttpPost] [Route("GetUserPosts")] [AllowAnonymous] diff --git a/src/Web/DevHive.Web/Controllers/FriendsController.cs b/src/Web/DevHive.Web/Controllers/FriendsController.cs new file mode 100644 index 0000000..318ae64 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/FriendsController.cs @@ -0,0 +1,54 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Common.Models.Identity; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.User; +using DevHive.Web.Models.User; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using NSwag.Annotations; + +namespace DevHive.Web.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class FriendsController + { + private readonly IFriendsService _friendsService; + private readonly IMapper _mapper; + private readonly IJwtService _jwtService; + + public FriendsController(IFriendsService friendsService, IMapper mapper, IJwtService jwtService) + { + this._friendsService = friendsService; + this._mapper = mapper; + this._jwtService = jwtService; + } + + [HttpPost] + [Authorize(Roles = "User,Admin")] + public async Task<IActionResult> AddFriend(Guid userId, string friendUsername, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + return (await this._friendsService.AddFriend(userId, friendUsername)) ? + new OkResult() : + new BadRequestResult(); + } + + [HttpDelete] + [Authorize(Roles = "User,Admin")] + public async Task<IActionResult> RemoveFriend(Guid userId, string friendUsername, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + return (await this._friendsService.RemoveFriend(userId, friendUsername)) ? + new OkResult() : + new BadRequestResult(); + } + } +} diff --git a/src/DevHive.Web/Controllers/LanguageController.cs b/src/Web/DevHive.Web/Controllers/LanguageController.cs index 5b0d5de..665fb66 100644 --- a/src/DevHive.Web/Controllers/LanguageController.cs +++ b/src/Web/DevHive.Web/Controllers/LanguageController.cs @@ -10,6 +10,9 @@ using Microsoft.AspNetCore.Mvc; namespace DevHive.Web.Controllers { + /// <summary> + /// All endpoints for interacting with the language layer + /// </summary> [ApiController] [Route("/api/[controller]")] public class LanguageController @@ -23,6 +26,11 @@ namespace DevHive.Web.Controllers this._languageMapper = mapper; } + /// <summary> + /// Create a new language, so users can have a choice. Admin only! + /// </summary> + /// <param name="createLanguageWebModel">The new language's parametars</param> + /// <returns>The new language's Id</returns> [HttpPost] [Authorize(Roles = "Admin")] public async Task<IActionResult> Create([FromBody] CreateLanguageWebModel createLanguageWebModel) @@ -36,6 +44,11 @@ namespace DevHive.Web.Controllers new OkObjectResult(new { Id = id }); } + /// <summary> + /// Query full language data by Id + /// </summary> + /// <param name="id">The language's Id</param> + /// <returns>Full language data</returns> [HttpGet] [AllowAnonymous] public async Task<IActionResult> GetById(Guid id) @@ -46,6 +59,10 @@ namespace DevHive.Web.Controllers return new OkObjectResult(languageWebModel); } + /// <summary> + /// Query all languages in the database + /// </summary> + /// <returns>All languages in the database</returns> [HttpGet] [Route("GetLanguages")] [Authorize(Roles = "User,Admin")] @@ -57,6 +74,12 @@ namespace DevHive.Web.Controllers return new OkObjectResult(languageWebModels); } + /// <summary> + /// Alter language's properties. Admin only! + /// </summary> + /// <param name="id">The language's Id</param> + /// <param name="updateModel">The langauge's new parametars</param> + /// <returns>Ok result</returns> [HttpPut] [Authorize(Roles = "Admin")] public async Task<IActionResult> Update(Guid id, [FromBody] UpdateLanguageWebModel updateModel) @@ -72,11 +95,16 @@ namespace DevHive.Web.Controllers return new OkResult(); } + /// <summary> + /// Delete a language. Admin only! + /// </summary> + /// <param name="langaugeId">The language's Id</param> + /// <returns>Ok result</returns> [HttpDelete] [Authorize(Roles = "Admin")] - public async Task<IActionResult> Delete(Guid id) + public async Task<IActionResult> Delete(Guid langaugeId) { - bool result = await this._languageService.DeleteLanguage(id); + bool result = await this._languageService.DeleteLanguage(langaugeId); if (!result) return new BadRequestObjectResult("Could not delete Language"); diff --git a/src/DevHive.Web/Controllers/PostController.cs b/src/Web/DevHive.Web/Controllers/PostController.cs index d3fdbf6..44b291d 100644 --- a/src/DevHive.Web/Controllers/PostController.cs +++ b/src/Web/DevHive.Web/Controllers/PostController.cs @@ -6,9 +6,13 @@ using DevHive.Web.Models.Post; using DevHive.Services.Models.Post; using Microsoft.AspNetCore.Authorization; using DevHive.Services.Interfaces; +using DevHive.Common.Jwt.Interfaces; namespace DevHive.Web.Controllers { + /// <summary> + /// All endpoints for interacting with the post layer + /// </summary> [ApiController] [Route("/api/[controller]")] [Authorize(Roles = "User,Admin")] @@ -16,18 +20,27 @@ namespace DevHive.Web.Controllers { private readonly IPostService _postService; private readonly IMapper _postMapper; + private readonly IJwtService _jwtService; - public PostController(IPostService postService, IMapper postMapper) + public PostController(IPostService postService, IMapper postMapper, IJwtService jwtService) { this._postService = postService; this._postMapper = postMapper; + this._jwtService = jwtService; } #region Create + /// <summary> + /// Create a new post + /// </summary> + /// <param name="userId">The user's Id</param> + /// <param name="createPostWebModel">The new post's data</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>New post's Id</returns> [HttpPost] public async Task<IActionResult> Create(Guid userId, [FromForm] CreatePostWebModel createPostWebModel, [FromHeader] string authorization) { - if (!await this._postService.ValidateJwtForCreating(userId, authorization)) + if (!this._jwtService.ValidateToken(userId, authorization)) return new UnauthorizedResult(); CreatePostServiceModel createPostServiceModel = @@ -43,6 +56,11 @@ namespace DevHive.Web.Controllers #endregion #region Read + /// <summary> + /// Query full post's data by it's Id + /// </summary> + /// <param name="id">The post's Id</param> + /// <returns>Full data model of the post</returns> [HttpGet] [AllowAnonymous] public async Task<IActionResult> GetById(Guid id) @@ -55,9 +73,19 @@ namespace DevHive.Web.Controllers #endregion #region Update + /// <summary> + /// Update post's data. Creator only! + /// </summary> + /// <param name="userId">The post creator's Id</param> + /// <param name="updatePostWebModel">The new params of the post</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>The post's Id</returns> [HttpPut] public async Task<IActionResult> Update(Guid userId, [FromForm] UpdatePostWebModel updatePostWebModel, [FromHeader] string authorization) { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + if (!await this._postService.ValidateJwtForPost(updatePostWebModel.PostId, authorization)) return new UnauthorizedResult(); @@ -74,13 +102,19 @@ namespace DevHive.Web.Controllers #endregion #region Delete + /// <summary> + /// Delete a post. Creator only! + /// </summary> + /// <param name="postId">Post's Id</param> + /// <param name="authorization">JWT Bearer token</param> + /// <returns>Ok result</returns> [HttpDelete] - public async Task<IActionResult> Delete(Guid id, [FromHeader] string authorization) + public async Task<IActionResult> Delete(Guid postId, [FromHeader] string authorization) { - if (!await this._postService.ValidateJwtForPost(id, authorization)) + if (!await this._postService.ValidateJwtForPost(postId, authorization)) return new UnauthorizedResult(); - return await this._postService.DeletePost(id) ? + return await this._postService.DeletePost(postId) ? new OkResult() : new BadRequestObjectResult("Could not delete Post"); } diff --git a/src/Web/DevHive.Web/Controllers/ProfilePictureController.cs b/src/Web/DevHive.Web/Controllers/ProfilePictureController.cs new file mode 100644 index 0000000..8474df5 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/ProfilePictureController.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.ProfilePicture; +using DevHive.Common.Jwt.Interfaces; +using AutoMapper; +using DevHive.Web.Models.ProfilePicture; + +namespace DevHive.Web.Controllers +{ + /// <summary> + /// All endpoints for interacting with the profile picture layer + /// </summary> + [ApiController] + [Route("api/[controller]")] + public class ProfilePictureController + { + private readonly IProfilePictureService _profilePictureService; + private readonly IJwtService _jwtService; + private readonly IMapper _profilePictureMapper; + + public ProfilePictureController(IProfilePictureService profilePictureService, IJwtService jwtService, IMapper profilePictureMapper) + { + this._profilePictureService = profilePictureService; + this._jwtService = jwtService; + this._profilePictureMapper = profilePictureMapper; + } + + /// <summary> + /// Get the URL of user's profile picture + /// </summary> + /// <param name="profilePictureId">The profile picture's Id</param> + /// <param name="authorization">JWT Bearer Token</param> + /// <returns>The URL of the profile picture</returns> + [HttpGet] + [AllowAnonymous] + public async Task<IActionResult> ReadProfilePicture(Guid profilePictureId) + { + string profilePicURL = await this._profilePictureService.GetProfilePictureById(profilePictureId); + return new OkObjectResult(new { ProfilePictureURL = profilePicURL} ); + } + + /// <summary> + /// Alter the profile picture of a user + /// </summary> + /// <param name="userId">The user's Id</param> + /// <param name="profilePictureWebModel">The new profile picture</param> + /// <param name="authorization">JWT Bearer Token</param> + /// <returns>The URL of the new profile picture</returns> + [HttpPut] + [Authorize(Roles = "User,Admin")] + public async Task<IActionResult> UpdateProfilePicture(Guid userId, [FromForm] ProfilePictureWebModel profilePictureWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + ProfilePictureServiceModel profilePictureServiceModel = this._profilePictureMapper.Map<ProfilePictureServiceModel>(profilePictureWebModel); + profilePictureServiceModel.UserId = userId; + + string url = await this._profilePictureService.UpdateProfilePicture(profilePictureServiceModel); + return new OkObjectResult(new { URL = url }); + } + } +} diff --git a/src/Web/DevHive.Web/Controllers/RatingController.cs b/src/Web/DevHive.Web/Controllers/RatingController.cs new file mode 100644 index 0000000..7d21795 --- /dev/null +++ b/src/Web/DevHive.Web/Controllers/RatingController.cs @@ -0,0 +1,98 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using DevHive.Common.Jwt.Interfaces; +using DevHive.Services.Interfaces; +using DevHive.Services.Models.Rating; +using DevHive.Web.Models.Rating; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace DevHive.Web.Controllers +{ + [ApiController] + [Authorize(Roles = "Admin,User")] + [Route("api/[controller]")] + public class RatingController + { + private readonly IRatingService _rateService; + private readonly IMapper _mapper; + private readonly IJwtService _jwtService; + + public RatingController(IRatingService rateService, IMapper mapper, IJwtService jwtService) + { + this._rateService = rateService; + this._mapper = mapper; + this._jwtService = jwtService; + } + + [HttpPost] + public async Task<IActionResult> RatePost(Guid userId, [FromBody] CreateRatingWebModel createRatingWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + CreateRatingServiceModel ratePostServiceModel = this._mapper.Map<CreateRatingServiceModel>(createRatingWebModel); + ratePostServiceModel.UserId = userId; + + Guid id = await this._rateService.RatePost(ratePostServiceModel); + + if (Guid.Empty == id) + return new BadRequestResult(); + + return new OkObjectResult(new { Id = id }); + } + + [HttpGet] + public async Task<IActionResult> GetRatingById(Guid id) + { + ReadRatingServiceModel readRatingServiceModel = await this._rateService.GetRatingById(id); + ReadRatingWebModel readPostRatingWebModel = this._mapper.Map<ReadRatingWebModel>(readRatingServiceModel); + + return new OkObjectResult(readPostRatingWebModel); + } + + [HttpGet] + [Route("GetByUserAndPost")] + public async Task<IActionResult> GetRatingByUserAndPost(Guid userId, Guid postId) + { + ReadRatingServiceModel readRatingServiceModel = await this._rateService.GetRatingByPostAndUser(userId, postId); + ReadRatingWebModel readPostRatingWebModel = this._mapper.Map<ReadRatingWebModel>(readRatingServiceModel); + + return new OkObjectResult(readPostRatingWebModel); + } + + [HttpPut] + public async Task<IActionResult> UpdateRating(Guid userId, Guid postId, [FromBody] UpdateRatingWebModel updateRatingWebModel, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + UpdateRatingServiceModel updateRatingServiceModel = + this._mapper.Map<UpdateRatingServiceModel>(updateRatingWebModel); + updateRatingServiceModel.UserId = userId; + updateRatingServiceModel.PostId = postId; + + ReadRatingServiceModel readRatingServiceModel = await this._rateService.UpdateRating(updateRatingServiceModel); + + if (readRatingServiceModel == null) + return new BadRequestResult(); + else + { + ReadRatingWebModel readRatingWebModel = this._mapper.Map<ReadRatingWebModel>(readRatingServiceModel); + return new OkObjectResult(readRatingWebModel); + } + } + + [HttpDelete] + public async Task<IActionResult> DeleteRating(Guid userId, Guid ratingId, [FromHeader] string authorization) + { + if (!this._jwtService.ValidateToken(userId, authorization)) + return new UnauthorizedResult(); + + return await this._rateService.DeleteRating(ratingId) ? + new OkResult() : + new BadRequestObjectResult("Could not delete Rating"); + } + } +} diff --git a/src/DevHive.Web/Controllers/RoleController.cs b/src/Web/DevHive.Web/Controllers/RoleController.cs index 0d2a2eb..ebb305e 100644 --- a/src/DevHive.Web/Controllers/RoleController.cs +++ b/src/Web/DevHive.Web/Controllers/RoleController.cs @@ -1,14 +1,17 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using DevHive.Web.Models.Identity.Role; +using DevHive.Web.Models.Role; using AutoMapper; using System; using DevHive.Services.Interfaces; -using DevHive.Services.Models.Identity.Role; +using DevHive.Services.Models.Role; using Microsoft.AspNetCore.Authorization; namespace DevHive.Web.Controllers { + /// <summary> + /// All endpoints for interacting with the roles layer + /// </summary> [ApiController] [Route("/api/[controller]")] public class RoleController @@ -22,6 +25,11 @@ namespace DevHive.Web.Controllers this._roleMapper = mapper; } + /// <summary> + /// Create a new role for the roles hierarchy. Admin only! + /// </summary> + /// <param name="createRoleWebModel">The new role's parametars</param> + /// <returns>The new role's Id</returns> [HttpPost] [Authorize(Roles = "Admin")] public async Task<IActionResult> Create([FromBody] CreateRoleWebModel createRoleWebModel) @@ -36,6 +44,11 @@ namespace DevHive.Web.Controllers new OkObjectResult(new { Id = id }); } + /// <summary> + /// Get a role's full data, querying it by it's Id + /// </summary> + /// <param name="id">The role's Id</param> + /// <returns>Full info of the role</returns> [HttpGet] [Authorize(Roles = "User,Admin")] public async Task<IActionResult> GetById(Guid id) @@ -46,6 +59,12 @@ namespace DevHive.Web.Controllers return new OkObjectResult(roleWebModel); } + /// <summary> + /// Update a role's parametars. Admin only! + /// </summary> + /// <param name="id">The role's Id</param> + /// <param name="updateRoleWebModel">The new parametrats for that role</param> + /// <returns>Ok result</returns> [HttpPut] [Authorize(Roles = "Admin")] public async Task<IActionResult> Update(Guid id, [FromBody] UpdateRoleWebModel updateRoleWebModel) @@ -62,6 +81,11 @@ namespace DevHive.Web.Controllers return new OkResult(); } + /// <summary> + /// Delete a role. Admin only! + /// </summary> + /// <param name="id">The role's Id</param> + /// <returns>Ok result</returns> [HttpDelete] [Authorize(Roles = "Admin")] public async Task<IActionResult> Delete(Guid id) diff --git a/src/DevHive.Web/Controllers/TechnologyController.cs b/src/Web/DevHive.Web/Controllers/TechnologyController.cs index e507899..ecf2bd7 100644 --- a/src/DevHive.Web/Controllers/TechnologyController.cs +++ b/src/Web/DevHive.Web/Controllers/TechnologyController.cs @@ -10,6 +10,9 @@ using Microsoft.AspNetCore.Mvc; namespace DevHive.Web.Controllers { + /// <summary> + /// All endpoints for interacting with the technology layer + /// </summary> [ApiController] [Route("/api/[controller]")] public class TechnologyController @@ -23,6 +26,11 @@ namespace DevHive.Web.Controllers this._technologyMapper = technologyMapper; } + /// <summary> + /// Create a new technology, so users can have a choice. Admin only! + /// </summary> + /// <param name="createTechnologyWebModel">Data for the new technology</param> + /// <returns>The new technology's Id</returns> [HttpPost] [Authorize(Roles = "Admin")] public async Task<IActionResult> Create([FromBody] CreateTechnologyWebModel createTechnologyWebModel) @@ -36,6 +44,11 @@ namespace DevHive.Web.Controllers new OkObjectResult(new { Id = id }); } + /// <summary> + /// Get technology's data by it's Id + /// </summary> + /// <param name="id">The technology's Id</param> + /// <returns>The technology's full data</returns> [HttpGet] [AllowAnonymous] public async Task<IActionResult> GetById(Guid id) @@ -46,6 +59,10 @@ namespace DevHive.Web.Controllers return new OkObjectResult(readTechnologyWebModel); } + /// <summary> + /// Get all technologies from our database + /// </summary> + /// <returns>All technologies</returns> [HttpGet] [Route("GetTechnologies")] [Authorize(Roles = "User,Admin")] @@ -57,6 +74,12 @@ namespace DevHive.Web.Controllers return new OkObjectResult(languageWebModels); } + /// <summary> + /// Alter a technology's parameters. Admin only! + /// </summary> + /// <param name="id">Technology's Id</param> + /// <param name="updateModel">The new parametars</param> + /// <returns>Ok result</returns> [HttpPut] [Authorize(Roles = "Admin")] public async Task<IActionResult> Update(Guid id, [FromBody] UpdateTechnologyWebModel updateModel) @@ -72,6 +95,11 @@ namespace DevHive.Web.Controllers return new OkResult(); } + /// <summary> + /// Delete a etchnology from the database. Admin only! + /// </summary> + /// <param name="id">The technology's Id</param> + /// <returns>Ok result</returns> [HttpDelete] [Authorize(Roles = "Admin")] public async Task<IActionResult> Delete(Guid id) diff --git a/src/DevHive.Web/Controllers/UserController.cs b/src/Web/DevHive.Web/Controllers/UserController.cs index 109bbaa..4d01447 100644 --- a/src/DevHive.Web/Controllers/UserController.cs +++ b/src/Web/DevHive.Web/Controllers/UserController.cs @@ -1,63 +1,88 @@ using System; using System.Threading.Tasks; using AutoMapper; -using DevHive.Services.Models.Identity.User; -using DevHive.Web.Models.Identity.User; +using DevHive.Services.Models.User; +using DevHive.Web.Models.User; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using DevHive.Common.Models.Identity; using DevHive.Services.Interfaces; -using Microsoft.Extensions.Hosting; +using DevHive.Common.Jwt.Interfaces; +using NSwag.Annotations; namespace DevHive.Web.Controllers { + /// <summary> + /// All endpoints for integration with the User + /// </summary> [ApiController] [Route("/api/[controller]")] + [OpenApiController("User Controller")] public class UserController : ControllerBase { private readonly IUserService _userService; private readonly IMapper _userMapper; + private readonly IJwtService _jwtService; - public UserController(IUserService userService, IMapper mapper) + public UserController(IUserService userService, IMapper mapper, IJwtService jwtService) { this._userService = userService; this._userMapper = mapper; + this._jwtService = jwtService; } #region Authentication + /// <summary> + /// Login endpoint for the DevHive Social Platform + /// </summary> + /// <param name="loginModel">Login model with username and password</param> + /// <returns>A JWT Token for further validation</returns> [HttpPost] - [Route("Login")] [AllowAnonymous] + [Route("Login")] + [OpenApiTags("Authorization")] public async Task<IActionResult> Login([FromBody] LoginWebModel loginModel) { LoginServiceModel loginServiceModel = this._userMapper.Map<LoginServiceModel>(loginModel); - TokenModel TokenModel = await this._userService.LoginUser(loginServiceModel); - TokenWebModel tokenWebModel = this._userMapper.Map<TokenWebModel>(TokenModel); + TokenModel tokenModel = await this._userService.LoginUser(loginServiceModel); + TokenWebModel tokenWebModel = this._userMapper.Map<TokenWebModel>(tokenModel); return new OkObjectResult(tokenWebModel); } + /// <summary> + /// Register a new User in the DevHive Social Platform + /// </summary> + /// <param name="registerModel">Register model with the new data to provide</param> + /// <returns>A JWT Token for further validation</returns> [HttpPost] - [Route("Register")] [AllowAnonymous] + [Route("Register")] + [OpenApiTag("Authorization")] public async Task<IActionResult> Register([FromBody] RegisterWebModel registerModel) { RegisterServiceModel registerServiceModel = this._userMapper.Map<RegisterServiceModel>(registerModel); - TokenModel TokenModel = await this._userService.RegisterUser(registerServiceModel); - TokenWebModel tokenWebModel = this._userMapper.Map<TokenWebModel>(TokenModel); + TokenModel tokenModel = await this._userService.RegisterUser(registerServiceModel); + TokenWebModel tokenWebModel = this._userMapper.Map<TokenWebModel>(tokenModel); return new CreatedResult("Register", tokenWebModel); } #endregion #region Read + /// <summary> + /// Get a User's information using the Guid + /// </summary> + /// <param name="id">User's Id</param> + /// <param name="authorization">The JWT Token, contained in the header and used for validation</param> + /// <returns>A full User's read model</returns> [HttpGet] [Authorize(Roles = "User,Admin")] public async Task<IActionResult> GetById(Guid id, [FromHeader] string authorization) { - if (!await this._userService.ValidJWT(id, authorization)) + if (!this._jwtService.ValidateToken(id, authorization)) return new UnauthorizedResult(); UserServiceModel userServiceModel = await this._userService.GetUserById(id); @@ -66,6 +91,11 @@ namespace DevHive.Web.Controllers return new OkObjectResult(userWebModel); } + /// <summary> + /// Get a User's profile using his username. Does NOT require authorization + /// </summary> + /// <param name="username">User's username</param> + /// <returns>A trimmed version of the full User's read model</returns> [HttpGet] [Route("GetUser")] [AllowAnonymous] @@ -79,11 +109,18 @@ namespace DevHive.Web.Controllers #endregion #region Update + /// <summary> + /// Full update on User's data. A PUSTINQK can only edit his account + /// </summary> + /// <param name="id">The User's Id</param> + /// <param name="updateUserWebModel">A full User update model</param> + /// <param name="authorization">The JWT Token, contained in the header and used for validation</param> + /// <returns>A full User's read model</returns> [HttpPut] [Authorize(Roles = "User,Admin")] public async Task<IActionResult> Update(Guid id, [FromBody] UpdateUserWebModel updateUserWebModel, [FromHeader] string authorization) { - if (!await this._userService.ValidJWT(id, authorization)) + if (!this._jwtService.ValidateToken(id, authorization)) return new UnauthorizedResult(); UpdateUserServiceModel updateUserServiceModel = this._userMapper.Map<UpdateUserServiceModel>(updateUserWebModel); @@ -94,31 +131,20 @@ namespace DevHive.Web.Controllers return new AcceptedResult("UpdateUser", userWebModel); } - - [HttpPut] - [Route("ProfilePicture")] - [Authorize(Roles = "User,Admin")] - public async Task<IActionResult> UpdateProfilePicture(Guid userId, [FromForm] UpdateProfilePictureWebModel updateProfilePictureWebModel, [FromHeader] string authorization) - { - if (!await this._userService.ValidJWT(userId, authorization)) - return new UnauthorizedResult(); - - UpdateProfilePictureServiceModel updateProfilePictureServiceModel = this._userMapper.Map<UpdateProfilePictureServiceModel>(updateProfilePictureWebModel); - updateProfilePictureServiceModel.UserId = userId; - - ProfilePictureServiceModel profilePictureServiceModel = await this._userService.UpdateProfilePicture(updateProfilePictureServiceModel); - ProfilePictureWebModel profilePictureWebModel = this._userMapper.Map<ProfilePictureWebModel>(profilePictureServiceModel); - - return new AcceptedResult("UpdateProfilePicture", profilePictureWebModel); - } #endregion #region Delete + /// <summary> + /// Delete a User with his Id. A PUSTINQK can only delete his account. An Admin can delete all accounts + /// </summary> + /// <param name="id">The User's Id</param> + /// <param name="authorization">The JWT Token, contained in the header and used for validation</param> + /// <returns>Ok, BadRequest or Unauthorized</returns> [HttpDelete] [Authorize(Roles = "User,Admin")] public async Task<IActionResult> Delete(Guid id, [FromHeader] string authorization) { - if (!await this._userService.ValidJWT(id, authorization)) + if (!this._jwtService.ValidateToken(id, authorization)) return new UnauthorizedResult(); bool result = await this._userService.DeleteUser(id); @@ -129,7 +155,13 @@ namespace DevHive.Web.Controllers } #endregion + /// <summary> + /// We don't talk about that, NIGGA! + /// </summary> + /// <param name="userId"></param> + /// <returns></returns> [HttpPost] + [OpenApiIgnore] [Authorize(Roles = "User,Admin")] [Route("SuperSecretPromotionToAdmin")] public async Task<IActionResult> SuperSecretPromotionToAdmin(Guid userId) diff --git a/src/Web/DevHive.Web/DevHive.Web.csproj b/src/Web/DevHive.Web/DevHive.Web.csproj new file mode 100644 index 0000000..5b3a920 --- /dev/null +++ b/src/Web/DevHive.Web/DevHive.Web.csproj @@ -0,0 +1,43 @@ +<Project Sdk="Microsoft.NET.Sdk.Web"> + <PropertyGroup> + <TargetFramework>net5.0</TargetFramework> + </PropertyGroup> + <PropertyGroup> + <EnableNETAnalyzers>true</EnableNETAnalyzers> + <AnalysisLevel>latest</AnalysisLevel> + <GenerateDocumentationFile>true</GenerateDocumentationFile> + <AllowUntrustedCertificate>true</AllowUntrustedCertificate> + </PropertyGroup> + <ItemGroup> + <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.4" NoWarn="NU1605"/> + <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.4" NoWarn="NU1605"/> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.4"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.2"/> + <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1"/> + <PackageReference Include="AutoMapper" Version="10.1.1"/> + <PackageReference Include="Newtonsoft.Json" Version="13.0.1"/> + <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.4"/> + <PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934"/> + <PackageReference Include="NSwag.AspNetCore" Version="13.10.8"/> + <PackageReference Include="NSwag.Generation.AspNetCore" Version="13.10.8"/> + <PackageReference Include="NSwag.Annotations" Version="13.10.8"/> + <PackageReference Include="NSwag.Core" Version="13.10.8"/> + <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.1.1"/> + <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.1.1"/> + <PackageReference Include="NSwag.SwaggerGeneration.WebApi" Version="12.3.1"/> + <PackageReference Include="Serilog.AspNetCore" Version="4.1.0"/> + <PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0"/> + <PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3"/> + <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0"/> + <PackageReference Include="Serilog.Enrichers.Process" Version="2.0.1"/> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\DevHive.Web.Models\DevHive.Web.Models.csproj"/> + <ProjectReference Include="..\..\Services\DevHive.Services\DevHive.Services.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common.Models\DevHive.Common.Models.csproj"/> + <ProjectReference Include="..\..\Common\DevHive.Common\DevHive.Common.csproj"/> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/src/DevHive.Web/Middleware/ExceptionMiddleware.cs b/src/Web/DevHive.Web/Middleware/ExceptionMiddleware.cs index cb6d4ca..680d824 100644 --- a/src/DevHive.Web/Middleware/ExceptionMiddleware.cs +++ b/src/Web/DevHive.Web/Middleware/ExceptionMiddleware.cs @@ -2,25 +2,18 @@ using System; using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; +using Newtonsoft.Json; namespace DevHive.Web.Middleware { public class ExceptionMiddleware { private readonly RequestDelegate _next; - // private readonly ILogger _logger; public ExceptionMiddleware(RequestDelegate next) { this._next = next; - // this._logger = logger; } - // public ExceptionMiddleware(RequestDelegate next, ILogger logger) - // { - // this._logger = logger; - // this._next = next; - // } public async Task InvokeAsync(HttpContext httpContext) { @@ -30,21 +23,22 @@ namespace DevHive.Web.Middleware } catch (Exception ex) { - // this._logger.LogError($"Something went wrong: {ex}"); await HandleExceptionAsync(httpContext, ex); } } - private Task HandleExceptionAsync(HttpContext context, Exception exception) + private static Task HandleExceptionAsync(HttpContext context, Exception exception) { context.Response.ContentType = "application/json"; context.Response.StatusCode = (int)HttpStatusCode.BadRequest; - return context.Response.WriteAsync(new + var problems = new { - StatusCode = context.Response.StatusCode, - Message = exception.Message - }.ToString()); + errors = new { Exception = new String[] { exception.Message } } + }; + + string message = JsonConvert.SerializeObject(problems); + return context.Response.WriteAsync(message); } } } diff --git a/src/Web/DevHive.Web/Program.cs b/src/Web/DevHive.Web/Program.cs new file mode 100644 index 0000000..e7c47a9 --- /dev/null +++ b/src/Web/DevHive.Web/Program.cs @@ -0,0 +1,49 @@ +using System; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Configuration; +using Serilog; + +namespace DevHive.Web +{ + #pragma warning disable IDE0055, S1118 + + public class Program + { + private const int HTTP_PORT = 5000; + + public static void Main(string[] args) + { + var config = new ConfigurationBuilder() + .AddJsonFile("appsettings.json") + .Build(); + + Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(config) + .CreateLogger(); + + try + { + Log.Information("Application Starting Up"); + CreateHostBuilder(args).Build().Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "The application failed to start correctly."); + } + finally + { + Log.CloseAndFlush(); + } + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseSerilog() + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.ConfigureKestrel(opt => opt.ListenLocalhost(HTTP_PORT)); + webBuilder.UseStartup<Startup>(); + }); + } +} diff --git a/src/DevHive.Web/Properties/launchSettings.json b/src/Web/DevHive.Web/Properties/launchSettings.json index 2b65d0b..2b65d0b 100644 --- a/src/DevHive.Web/Properties/launchSettings.json +++ b/src/Web/DevHive.Web/Properties/launchSettings.json diff --git a/src/DevHive.Web/Startup.cs b/src/Web/DevHive.Web/Startup.cs index 46521cf..49fa408 100644 --- a/src/DevHive.Web/Startup.cs +++ b/src/Web/DevHive.Web/Startup.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using DevHive.Web.Configurations.Extensions; using Newtonsoft.Json; +using Serilog; namespace DevHive.Web { @@ -28,11 +29,12 @@ namespace DevHive.Web x.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); + services.DependencyInjectionConfiguration(this.Configuration); services.DatabaseConfiguration(Configuration); services.SwaggerConfiguration(); services.JWTConfiguration(Configuration); services.AutoMapperConfiguration(); - services.DependencyInjectionConfiguration(this.Configuration); + services.ConfigureExceptionHandler(this.Configuration); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -46,8 +48,8 @@ namespace DevHive.Web if (env.IsDevelopment()) { - app.UseDeveloperExceptionPage(); - app.UseSwaggerConfiguration(); + app.UseExceptionHandlerMiddlewareConfiguration(); + // app.UseDeveloperExceptionPage(); } else { @@ -55,9 +57,12 @@ namespace DevHive.Web app.UseExceptionHandlerMiddlewareConfiguration(); } + app.UseSwaggerConfiguration(); app.UseDatabaseConfiguration(); app.UseAutoMapperConfiguration(); + app.UseSerilogRequestLogging(); + app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( diff --git a/src/Web/DevHive.Web/appsettings.json b/src/Web/DevHive.Web/appsettings.json new file mode 100644 index 0000000..84d534d --- /dev/null +++ b/src/Web/DevHive.Web/appsettings.json @@ -0,0 +1,50 @@ +{ + "Jwt": { + "signingKey": "", + "validationIssuer": "", + "audience": "" + }, + "ConnectionStrings": { + "DEV": "Server=localhost;Port=5432;Database=DevHive_API;User Id=postgres;Password=;" + }, + "Cloud": { + "cloudName": "devhive", + "apiKey": "488664116365813", + "apiSecret": "" + }, + "Serilog": { + "Using": [], + "LevelSwitches": { + "$consoleSwitch": "Verbose", + "$fileSwitch": "Error" + }, + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "Enrich": [ + "FromLogContext", + "WithMachineName", + "WithProcessId", + "WithThreadId" + ], + "WriteTo": [ + { + "Name": "Console", + "Args": { + "levelSwitch": "$consoleSwitch" + } + }, + { + "Name": "File", + "Args": { + "path": "./Logs/errors.log", + "levelSwitch": "$fileSwitch" + } + } + ] + } +} diff --git a/src/docker-compose.debug.yml b/src/docker-compose.debug.yml new file mode 100644 index 0000000..9525082 --- /dev/null +++ b/src/docker-compose.debug.yml @@ -0,0 +1,17 @@ +# Please refer https://aka.ms/HTTPSinContainer on how to setup an https developer certificate for your ASP .NET Core service.
+
+version: '3.4'
+
+services:
+ devhiveweb:
+ image: devhiveweb
+ build:
+ context: .
+ dockerfile: ./Dockerfile
+ ports:
+ - 80
+ environment:
+ - ASPNETCORE_ENVIRONMENT=Development
+ - ASPNETCORE_URLS=http://+:80
+ volumes:
+ - ~/.vsdbg:/remote_debugger:rw
diff --git a/src/docker-compose.yml b/src/docker-compose.yml new file mode 100644 index 0000000..5c3aadd --- /dev/null +++ b/src/docker-compose.yml @@ -0,0 +1,12 @@ +# Please refer https://aka.ms/HTTPSinContainer on how to setup an https developer certificate for your ASP .NET Core service.
+
+version: '3.4'
+
+services:
+ devhiveweb:
+ image: devhiveweb
+ build:
+ context: .
+ dockerfile: ./Dockerfile
+ ports:
+ - 80
|
