From 799d7c44d87515a2df8eec98b335e6caa88adaec Mon Sep 17 00:00:00 2001 From: WilcoApp Date: Wed, 30 Oct 2024 18:16:34 +0000 Subject: [PATCH] Initial commit --- .devcontainer/devcontainer.json | 9 + .devcontainer/setup.sh | 8 +- .../java/.devcontainer/devcontainer.json | 4 - .framework/java/backend/.gitignore | 26 - .framework/java/backend/Dockerfile.aws | 9 - .framework/java/backend/LICENSE | 21 - .framework/java/backend/README.md | 35 - .framework/java/backend/build.gradle | 79 - .../backend/gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - .framework/java/backend/gradlew | 185 -- .framework/java/backend/gradlew.bat | 89 - .../io/spring/AnythinkMarketApplication.java | 12 - .../java/io/spring/JacksonCustomizations.java | 44 - .../main/java/io/spring/MyBatisConfig.java | 8 - .../backend/src/main/java/io/spring/Util.java | 7 - .../main/java/io/spring/api/CommentsApi.java | 94 - .../java/io/spring/api/CurrentUserApi.java | 58 - .../src/main/java/io/spring/api/ItemApi.java | 86 - .../java/io/spring/api/ItemFavoriteApi.java | 59 - .../src/main/java/io/spring/api/ItemsApi.java | 67 - .../src/main/java/io/spring/api/PingApi.java | 21 - .../main/java/io/spring/api/ProfileApi.java | 78 - .../src/main/java/io/spring/api/TagsApi.java | 26 - .../src/main/java/io/spring/api/UsersApi.java | 86 - .../exception/CustomizeExceptionHandler.java | 109 - .../spring/api/exception/ErrorResource.java | 18 - .../exception/ErrorResourceSerializer.java | 42 - .../api/exception/FieldErrorResource.java | 15 - .../InvalidAuthenticationException.java | 8 - .../exception/InvalidRequestException.java | 17 - .../exception/NoAuthorizationException.java | 7 - .../exception/ResourceNotFoundException.java | 7 - .../spring/api/security/JwtTokenFilter.java | 62 - .../api/security/WebSecurityConfig.java | 80 - .../application/CommentQueryService.java | 85 - .../application/CursorPageParameter.java | 40 - .../io/spring/application/CursorPager.java | 44 - .../io/spring/application/DateTimeCursor.java | 23 - .../spring/application/ItemQueryService.java | 185 -- .../main/java/io/spring/application/Node.java | 5 - .../main/java/io/spring/application/Page.java | 31 - .../io/spring/application/PageCursor.java | 18 - .../application/ProfileQueryService.java | 35 - .../spring/application/TagsQueryService.java | 16 - .../spring/application/UserQueryService.java | 17 - .../spring/application/data/CommentData.java | 29 - .../io/spring/application/data/ItemData.java | 33 - .../spring/application/data/ItemDataList.java | 20 - .../application/data/ItemFavoriteCount.java | 9 - .../spring/application/data/ProfileData.java | 17 - .../io/spring/application/data/UserData.java | 16 - .../application/data/UserWithToken.java | 20 - .../item/DuplicatedItemConstraint.java | 21 - .../item/DuplicatedItemValidator.java | 17 - .../application/item/ItemCommandService.java | 36 - .../spring/application/item/NewItemParam.java | 27 - .../application/item/UpdateItemParam.java | 16 - .../user/DuplicatedEmailConstraint.java | 16 - .../user/DuplicatedEmailValidator.java | 17 - .../user/DuplicatedUsernameConstraint.java | 16 - .../user/DuplicatedUsernameValidator.java | 17 - .../application/user/RegisterParam.java | 26 - .../application/user/UpdateUserCommand.java | 14 - .../application/user/UpdateUserParam.java | 25 - .../spring/application/user/UserService.java | 106 - .../java/io/spring/core/comment/Comment.java | 26 - .../core/comment/CommentRepository.java | 11 - .../io/spring/core/favorite/ItemFavorite.java | 18 - .../core/favorite/ItemFavoriteRepository.java | 11 - .../main/java/io/spring/core/item/Item.java | 74 - .../io/spring/core/item/ItemRepository.java | 14 - .../main/java/io/spring/core/item/Tag.java | 22 - .../core/service/AuthorizationService.java | 15 - .../io/spring/core/service/JwtService.java | 12 - .../io/spring/core/user/FollowRelation.java | 17 - .../main/java/io/spring/core/user/User.java | 50 - .../io/spring/core/user/UserRepository.java | 21 - .../mybatis/DateTimeHandler.java | 44 - .../mybatis/mapper/CommentMapper.java | 14 - .../mybatis/mapper/ItemFavoriteMapper.java | 14 - .../mybatis/mapper/ItemMapper.java | 29 - .../mybatis/mapper/UserMapper.java | 25 - .../readservice/CommentReadService.java | 18 - .../readservice/ItemFavoritesReadService.java | 19 - .../mybatis/readservice/ItemReadService.java | 42 - .../mybatis/readservice/TagReadService.java | 9 - .../mybatis/readservice/UserReadService.java | 13 - .../UserRelationshipQueryService.java | 16 - .../repository/MyBatisCommentRepository.java | 33 - .../MyBatisItemFavoriteRepository.java | 35 - .../repository/MyBatisItemRepository.java | 59 - .../repository/MyBatisUserRepository.java | 60 - .../service/DefaultJwtService.java | 54 - .../service/SendEventService.java | 77 - .../resources/application-test.properties | 4 - .../src/main/resources/application.properties | 24 - .../db/migration/V1__create_tables.sql | 50 - .../main/resources/mapper/CommentMapper.xml | 35 - .../resources/mapper/CommentReadService.xml | 44 - .../resources/mapper/ItemFavoriteMapper.xml | 21 - .../mapper/ItemFavoritesReadService.xml | 30 - .../src/main/resources/mapper/ItemMapper.xml | 82 - .../main/resources/mapper/ItemReadService.xml | 160 -- .../main/resources/mapper/TagReadService.xml | 7 - .../main/resources/mapper/TransferData.xml | 40 - .../src/main/resources/mapper/UserMapper.xml | 61 - .../main/resources/mapper/UserReadService.xml | 10 - .../mapper/UserRelationshipQueryService.xml | 18 - .../AnythinkMarketApplicationTests.java | 13 - .../src/test/java/io/spring/TestHelper.java | 42 - .../java/io/spring/api/CommentsApiTest.java | 141 -- .../io/spring/api/CurrentUserApiTest.java | 179 -- .../test/java/io/spring/api/ItemApiTest.java | 222 -- .../io/spring/api/ItemFavoriteApiTest.java | 103 - .../test/java/io/spring/api/ItemsApiTest.java | 174 -- .../java/io/spring/api/ListItemApiTest.java | 72 - .../java/io/spring/api/ProfileApiTest.java | 96 - .../io/spring/api/TestWithCurrentUser.java | 49 - .../test/java/io/spring/api/UsersApiTest.java | 271 --- .../comment/CommentQueryServiceTest.java | 76 - .../item/ItemQueryServiceTest.java | 226 -- .../profile/ProfileQueryServiceTest.java | 30 - .../application/tag/TagsQueryServiceTest.java | 25 - .../java/io/spring/core/item/ItemTest.java | 40 - .../io/spring/infrastructure/DbTestBase.java | 11 - .../comment/MyBatisCommentRepositoryTest.java | 26 - .../MyBatisItemFavoriteRepositoryTest.java | 33 - .../item/ItemRepositoryTransactionTest.java | 40 - .../item/MyBatisItemRepositoryTest.java | 72 - .../service/DefaultJwtServiceTest.java | 42 - .../user/MyBatisUserRepositoryTest.java | 73 - .framework/java/backend/start.sh | 6 - .../anythink-backend-deployment.yaml | 70 - .../charts/templates/database-deployment.yaml | 44 - .framework/java/charts/values.yaml | 70 - .framework/java/docker-compose.yml | 59 - .../node/.devcontainer/devcontainer.json | 4 - .framework/node/charts/.helmignore | 23 - .framework/node/charts/Chart.yaml | 24 - .framework/node/charts/templates/_helpers.yml | 24 - .../templates/anythink-backend-service.yaml | 14 - .../anythink-frontend-deployment.yaml | 55 - .../templates/anythink-frontend-service.yaml | 13 - .../node/charts/templates/database-pvc.yaml | 12 - .../charts/templates/database-service.yaml | 13 - .../python/.devcontainer/devcontainer.json | 4 - .framework/python/backend/.dockerignore | 20 - .framework/python/backend/.gitignore | 110 - .framework/python/backend/Dockerfile.aws | 15 - .framework/python/backend/LICENSE | 21 - .framework/python/backend/README.md | 30 - .framework/python/backend/alembic.ini | 36 - .framework/python/backend/app/__init__.py | 0 .framework/python/backend/app/api/__init__.py | 0 .../backend/app/api/dependencies/__init__.py | 0 .../app/api/dependencies/authentication.py | 111 - .../backend/app/api/dependencies/comments.py | 37 - .../backend/app/api/dependencies/database.py | 30 - .../backend/app/api/dependencies/items.py | 59 - .../backend/app/api/dependencies/profiles.py | 29 - .../python/backend/app/api/errors/__init__.py | 0 .../backend/app/api/errors/http_error.py | 7 - .../app/api/errors/validation_error.py | 28 - .../python/backend/app/api/routes/__init__.py | 0 .../python/backend/app/api/routes/api.py | 17 - .../backend/app/api/routes/authentication.py | 97 - .../python/backend/app/api/routes/comments.py | 67 - .../python/backend/app/api/routes/home.py | 11 - .../backend/app/api/routes/items/__init__.py | 0 .../backend/app/api/routes/items/api.py | 8 - .../app/api/routes/items/items_common.py | 104 - .../app/api/routes/items/items_resource.py | 122 - .../python/backend/app/api/routes/ping.py | 17 - .../python/backend/app/api/routes/profiles.py | 84 - .../python/backend/app/api/routes/tags.py | 15 - .../python/backend/app/api/routes/users.py | 73 - .../python/backend/app/core/__init__.py | 0 .framework/python/backend/app/core/config.py | 21 - .framework/python/backend/app/core/events.py | 25 - .framework/python/backend/app/core/logging.py | 25 - .../backend/app/core/settings/__init__.py | 0 .../python/backend/app/core/settings/app.py | 57 - .../python/backend/app/core/settings/base.py | 16 - .../backend/app/core/settings/development.py | 14 - .../backend/app/core/settings/production.py | 6 - .../python/backend/app/core/settings/test.py | 19 - .framework/python/backend/app/db/__init__.py | 0 .framework/python/backend/app/db/errors.py | 2 - .framework/python/backend/app/db/events.py | 29 - .../python/backend/app/db/migrations/env.py | 41 - .../backend/app/db/migrations/script.py.mako | 23 - .../versions/fdf8821871d7_main_tables.py | 217 -- .../python/backend/app/db/queries/__init__.py | 0 .../python/backend/app/db/queries/queries.py | 5 - .../python/backend/app/db/queries/queries.pyi | 125 - .../backend/app/db/queries/sql/comments.sql | 41 - .../backend/app/db/queries/sql/items.sql | 123 - .../backend/app/db/queries/sql/profiles.sql | 38 - .../backend/app/db/queries/sql/tags.sql | 9 - .../backend/app/db/queries/sql/users.sql | 49 - .../python/backend/app/db/queries/tables.py | 75 - .../backend/app/db/repositories/__init__.py | 0 .../backend/app/db/repositories/base.py | 10 - .../backend/app/db/repositories/comments.py | 103 - .../backend/app/db/repositories/items.py | 353 --- .../backend/app/db/repositories/profiles.py | 74 - .../backend/app/db/repositories/tags.py | 13 - .../backend/app/db/repositories/users.py | 81 - .framework/python/backend/app/db/seeds.py | 1 - .framework/python/backend/app/main.py | 47 - .../python/backend/app/models/__init__.py | 0 .../python/backend/app/models/common.py | 19 - .../backend/app/models/domain/__init__.py | 0 .../backend/app/models/domain/comments.py | 8 - .../python/backend/app/models/domain/items.py | 17 - .../backend/app/models/domain/profiles.py | 10 - .../backend/app/models/domain/rwmodel.py | 21 - .../python/backend/app/models/domain/users.py | 24 - .../backend/app/models/schemas/__init__.py | 0 .../backend/app/models/schemas/comments.py | 16 - .../backend/app/models/schemas/items.py | 46 - .../python/backend/app/models/schemas/jwt.py | 12 - .../backend/app/models/schemas/profiles.py | 7 - .../backend/app/models/schemas/rwschema.py | 6 - .../python/backend/app/models/schemas/tags.py | 7 - .../backend/app/models/schemas/users.py | 31 - .../python/backend/app/resources/__init__.py | 0 .../python/backend/app/resources/strings.py | 25 - .../python/backend/app/services/__init__.py | 0 .../backend/app/services/authentication.py | 20 - .../python/backend/app/services/event.py | 22 - .../python/backend/app/services/items.py | 23 - .framework/python/backend/app/services/jwt.py | 41 - .../python/backend/app/services/security.py | 16 - .framework/python/backend/poetry.lock | 2136 ----------------- .framework/python/backend/pyproject.toml | 72 - .framework/python/backend/requirements.txt | 43 - .framework/python/backend/runtime.txt | 1 - .framework/python/backend/scripts/format | 8 - .../python/backend/scripts/heroku_release.sh | 16 - .framework/python/backend/scripts/lint | 11 - .framework/python/backend/scripts/test | 6 - .../python/backend/scripts/test-cov-html | 6 - .framework/python/backend/seeds.sh | 3 - .framework/python/backend/setup.cfg | 88 - .framework/python/charts/.helmignore | 23 - .framework/python/charts/Chart.yaml | 24 - .../python/charts/templates/_helpers.yml | 24 - .../anythink-backend-deployment.yaml | 74 - .../templates/anythink-backend-service.yaml | 14 - .../anythink-frontend-deployment.yaml | 55 - .../templates/anythink-frontend-service.yaml | 13 - .../charts/templates/database-deployment.yaml | 44 - .../python/charts/templates/database-pvc.yaml | 12 - .../charts/templates/database-service.yaml | 13 - .framework/python/charts/values.yaml | 70 - .framework/python/docker-compose.yml | 66 - .../rails/.devcontainer/devcontainer.json | 4 - .framework/rails/backend/.gitignore | 30 - .framework/rails/backend/.ruby-version | 1 - .framework/rails/backend/Dockerfile.aws | 12 - .framework/rails/backend/Gemfile | 69 - .framework/rails/backend/Gemfile.lock | 258 -- .framework/rails/backend/README.md | 19 - .framework/rails/backend/Rakefile | 6 - .../backend/app/assets/config/manifest.js | 3 - .../rails/backend/app/assets/images/.keep | 0 .../backend/app/assets/javascripts/.keep | 0 .../backend/app/assets/stylesheets/.keep | 0 .../app/channels/application_cable/channel.rb | 4 - .../channels/application_cable/connection.rb | 4 - .../app/controllers/application_controller.rb | 48 - .../app/controllers/comments_controller.rb | 34 - .../backend/app/controllers/concerns/.keep | 0 .../app/controllers/favorites_controller.rb | 24 - .../app/controllers/follows_controller.rb | 21 - .../app/controllers/items_controller.rb | 98 - .../app/controllers/ping_controller.rb | 11 - .../app/controllers/profiles_controller.rb | 7 - .../controllers/registrations_controller.rb | 14 - .../app/controllers/sessions_controller.rb | 13 - .../app/controllers/tags_controller.rb | 7 - .../app/controllers/users_controller.rb | 21 - .../backend/app/helpers/application_helper.rb | 2 - .../rails/backend/app/jobs/application_job.rb | 2 - .../backend/app/mailers/application_mailer.rb | 4 - .../backend/app/models/application_record.rb | 3 - .../rails/backend/app/models/comment.rb | 8 - .../rails/backend/app/models/concerns/.keep | 0 .../rails/backend/app/models/favorite.rb | 6 - .framework/rails/backend/app/models/follow.rb | 14 - .framework/rails/backend/app/models/item.rb | 20 - .framework/rails/backend/app/models/user.rb | 40 - .../app/views/comments/_comment.json.jbuilder | 6 - .../app/views/comments/create.json.jbuilder | 5 - .../app/views/comments/index.json.jbuilder | 5 - .../devise/registrations/create.json.jbuilder | 5 - .../devise/sessions/create.json.jbuilder | 5 - .../app/views/items/_item.json.jbuilder | 9 - .../app/views/items/index.json.jbuilder | 7 - .../app/views/items/show.json.jbuilder | 5 - .../app/views/profiles/_profile.json.jbuilder | 5 - .../app/views/profiles/show.json.jbuilder | 5 - .../app/views/users/_user.json.jbuilder | 4 - .../app/views/users/show.json.jbuilder | 5 - .framework/rails/backend/bin/bundle | 3 - .../rails/backend/bin/heroku_release.sh | 25 - .framework/rails/backend/bin/rails | 9 - .framework/rails/backend/bin/rake | 9 - .framework/rails/backend/bin/setup | 36 - .framework/rails/backend/bin/spring | 17 - .framework/rails/backend/bin/update | 31 - .framework/rails/backend/bin/yarn | 11 - .framework/rails/backend/config.ru | 5 - .../rails/backend/config/application.rb | 22 - .framework/rails/backend/config/boot.rb | 4 - .framework/rails/backend/config/cable.yml | 10 - .../rails/backend/config/credentials.yml.enc | 1 - .framework/rails/backend/config/database.yml | 21 - .../rails/backend/config/environment.rb | 5 - .../config/environments/development.rb | 63 - .../backend/config/environments/production.rb | 93 - .../rails/backend/config/environments/test.rb | 46 - .../application_controller_renderer.rb | 8 - .../backend/config/initializers/assets.rb | 14 - .../initializers/backtrace_silencers.rb | 7 - .../initializers/content_security_policy.rb | 25 - .../config/initializers/cookies_serializer.rb | 5 - .../rails/backend/config/initializers/cors.rb | 9 - .../backend/config/initializers/devise.rb | 311 --- .../initializers/filter_parameter_logging.rb | 4 - .../config/initializers/inflections.rb | 16 - .../initializers/json_param_key_transform.rb | 17 - .../backend/config/initializers/mime_types.rb | 4 - .../config/initializers/wrap_parameters.rb | 14 - .../backend/config/locales/devise.en.yml | 65 - .../rails/backend/config/locales/en.yml | 33 - .../backend/config/locales/responders.en.yml | 12 - .framework/rails/backend/config/puma.rb | 34 - .framework/rails/backend/config/routes.rb | 27 - .framework/rails/backend/config/secrets.yml | 26 - .../rails/backend/config/secrets.yml.enc | 1 - .framework/rails/backend/config/spring.rb | 6 - .framework/rails/backend/config/storage.yml | 34 - .../20210412045707_devise_create_users.rb | 43 - ...10412045739_add_profile_fields_to_users.rb | 10 - .../db/migrate/20210412052128_create_items.rb | 17 - ...on_migration.acts_as_taggable_on_engine.rb | 33 - ...ique_indices.acts_as_taggable_on_engine.rb | 22 - ...ache_to_tags.acts_as_taggable_on_engine.rb | 17 - ...ggable_index.acts_as_taggable_on_engine.rb | 12 - ...or_tag_names.acts_as_taggable_on_engine.rb | 12 - .../20210412055201_create_favorites.rb | 12 - .../migrate/20210412061113_create_comments.rb | 13 - ...210412061614_acts_as_follower_migration.rb | 19 - .framework/rails/backend/db/schema.rb | 110 - .framework/rails/backend/db/seeds.rb | 7 - .framework/rails/backend/lib/assets/.keep | 0 .framework/rails/backend/lib/event.rb | 19 - .framework/rails/backend/lib/tasks/.keep | 0 .framework/rails/backend/log/.keep | 0 .framework/rails/backend/public/404.html | 67 - .framework/rails/backend/public/422.html | 67 - .framework/rails/backend/public/500.html | 66 - .framework/rails/backend/public/favicon.ico | 0 .framework/rails/backend/public/robots.txt | 1 - .framework/rails/backend/seeds.sh | 3 - .framework/rails/backend/start.sh | 3 - .framework/rails/backend/start_rails.sh | 5 - .framework/rails/backend/vendor/.keep | 0 .framework/rails/charts/.helmignore | 23 - .framework/rails/charts/Chart.yaml | 24 - .../rails/charts/templates/_helpers.yml | 24 - .../anythink-backend-deployment.yaml | 70 - .../templates/anythink-backend-service.yaml | 14 - .../anythink-frontend-deployment.yaml | 55 - .../templates/anythink-frontend-service.yaml | 13 - .../charts/templates/database-deployment.yaml | 42 - .../rails/charts/templates/database-pvc.yaml | 12 - .../charts/templates/database-service.yaml | 13 - .framework/rails/charts/values.yaml | 70 - .framework/rails/docker-compose.yml | 60 - .github/{.workflows => workflows}/k8s.yml | 0 .github/workflows/test_e2e_java.yml | 60 - .github/workflows/test_e2e_lint.yml | 28 - .github/workflows/test_e2e_node.yml | 46 - .github/workflows/test_e2e_python.yml | 68 - .github/workflows/test_e2e_rails.yml | 60 - .github/workflows/test_frontend_react.yml | 35 - .github/workflows/wilco-actions.yml | 34 + .wilco | 1 + .../node/backend => backend}/.gitignore | 0 backend/Dockerfile | 1 + .../node/backend => backend}/Dockerfile.aws | 0 .../node/backend => backend}/README.md | 0 {.framework/node/backend => backend}/app.js | 2 +- .../node/backend => backend}/config/index.js | 2 +- .../backend => backend}/config/passport.js | 0 .../node/backend => backend}/lib/event.js | 0 .../backend => backend}/models/Comment.js | 0 .../node/backend => backend}/models/Item.js | 0 .../node/backend => backend}/models/User.js | 0 .../node/backend => backend}/package.json | 0 .../node/backend => backend}/public/.keep | 0 backend/routes/api/comments.js | 5 + .../backend => backend}/routes/api/index.js | 1 + .../backend => backend}/routes/api/items.js | 0 .../backend => backend}/routes/api/ping.js | 0 .../routes/api/profiles.js | 0 .../backend => backend}/routes/api/tags.js | 0 .../backend => backend}/routes/api/users.js | 0 .../node/backend => backend}/routes/auth.js | 0 .../node/backend => backend}/routes/index.js | 0 .../node/backend => backend}/scripts/seeds.js | 0 {.framework/node/backend => backend}/seeds.sh | 0 {.framework/node/backend => backend}/start.sh | 0 .../tests/api-tests.postman.json | 0 .../tests/env-api-tests.postman.json | 0 .../node/backend => backend}/yarn.lock | 0 .../java/charts => charts}/.helmignore | 0 {.framework/java/charts => charts}/Chart.yaml | 0 .../charts => charts}/templates/_helpers.yml | 0 .../anythink-backend-deployment.yaml | 0 .../templates/anythink-backend-service.yaml | 0 .../anythink-frontend-deployment.yaml | 0 .../templates/anythink-frontend-service.yaml | 0 .../templates/database-deployment.yaml | 0 .../templates/database-pvc.yaml | 0 .../templates/database-service.yaml | 0 .../node/charts => charts}/values.yaml | 0 .../docker-compose.yml => docker-compose.yml | 9 +- .../react/frontend => frontend}/.eslintignore | 0 .../react/frontend => frontend}/.gitignore | 0 frontend/Dockerfile | 1 + .../frontend => frontend}/Dockerfile.aws | 0 .../frontend => frontend}/jest.config.js | 0 .../react/frontend => frontend}/package.json | 0 .../public/50precentoff.png | Bin .../frontend => frontend}/public/favicon.ico | Bin .../frontend => frontend}/public/index.html | 0 .../public/placeholder.png | Bin .../frontend => frontend}/public/style.css | 0 .../frontend => frontend}/public/sunray.jpeg | Bin .../public/verified_seller.svg | 0 .../react/frontend => frontend}/readme.md | 0 .../react/frontend => frontend}/src/agent.js | 0 .../src/components/App.js | 0 .../src/components/Editor.js | 0 .../src/components/Header.js | 0 .../src/components/Home/Banner.js | 0 .../src/components/Home/MainView.js | 0 .../src/components/Home/Tags.js | 0 .../src/components/Home/index.js | 0 .../src/components/Item/Comment.js | 0 .../src/components/Item/CommentContainer.js | 0 .../src/components/Item/CommentInput.js | 0 .../src/components/Item/CommentList.js | 0 .../src/components/Item/DeleteButton.js | 0 .../src/components/Item/ItemActions.js | 0 .../src/components/Item/ItemMeta.js | 0 .../src/components/Item/index.js | 0 .../src/components/Item/utils/ItemFetcher.js | 0 .../src/components/ItemList.js | 0 .../src/components/ItemPreview.js | 0 .../src/components/ListErrors.js | 0 .../src/components/ListPagination.js | 0 .../src/components/Login.js | 0 .../src/components/Profile.js | 0 .../src/components/ProfileFavorites.js | 0 .../src/components/Register.js | 0 .../src/components/Settings.js | 0 .../src/components/commons.js | 0 .../src/constants/actionTypes.js | 0 .../frontend => frontend}/src/custom.scss | 0 .../src/imgs/background.png | Bin .../frontend => frontend}/src/imgs/logo.png | Bin .../src/imgs/topbar_logo.png | Bin .../react/frontend => frontend}/src/index.js | 0 .../frontend => frontend}/src/middleware.js | 0 .../frontend => frontend}/src/reducer.js | 0 .../src/reducers/auth.js | 0 .../src/reducers/common.js | 0 .../src/reducers/editor.js | 0 .../src/reducers/home.js | 0 .../src/reducers/item.js | 0 .../src/reducers/itemList.js | 0 .../src/reducers/profile.js | 0 .../src/reducers/settings.js | 0 .../frontend => frontend}/src/setupTests.js | 0 .../react/frontend => frontend}/src/store.js | 0 .../src/tests/components/Header.test.js | 0 .../__snapshots__/Header.test.js.snap | 0 .../src/tests/item/CommentInput.test.js | 0 .../__snapshots__/CommentInput.test.js.snap | 0 .../react/frontend => frontend}/start.sh | 0 .../react/frontend => frontend}/yarn.lock | 0 readme.md | 2 +- 498 files changed, 61 insertions(+), 15618 deletions(-) create mode 100644 .devcontainer/devcontainer.json delete mode 100644 .framework/java/.devcontainer/devcontainer.json delete mode 100644 .framework/java/backend/.gitignore delete mode 100644 .framework/java/backend/Dockerfile.aws delete mode 100644 .framework/java/backend/LICENSE delete mode 100644 .framework/java/backend/README.md delete mode 100644 .framework/java/backend/build.gradle delete mode 100644 .framework/java/backend/gradle/wrapper/gradle-wrapper.jar delete mode 100644 .framework/java/backend/gradle/wrapper/gradle-wrapper.properties delete mode 100755 .framework/java/backend/gradlew delete mode 100644 .framework/java/backend/gradlew.bat delete mode 100644 .framework/java/backend/src/main/java/io/spring/AnythinkMarketApplication.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/JacksonCustomizations.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/MyBatisConfig.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/Util.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/CommentsApi.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/CurrentUserApi.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/ItemApi.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/ItemFavoriteApi.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/ItemsApi.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/PingApi.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/ProfileApi.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/TagsApi.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/UsersApi.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/exception/CustomizeExceptionHandler.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/exception/ErrorResource.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/exception/ErrorResourceSerializer.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/exception/FieldErrorResource.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/exception/InvalidAuthenticationException.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/exception/InvalidRequestException.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/exception/NoAuthorizationException.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/exception/ResourceNotFoundException.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/security/JwtTokenFilter.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/api/security/WebSecurityConfig.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/CommentQueryService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/CursorPageParameter.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/CursorPager.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/DateTimeCursor.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/ItemQueryService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/Node.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/Page.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/PageCursor.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/ProfileQueryService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/TagsQueryService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/UserQueryService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/data/CommentData.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/data/ItemData.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/data/ItemDataList.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/data/ItemFavoriteCount.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/data/ProfileData.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/data/UserData.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/data/UserWithToken.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/item/DuplicatedItemConstraint.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/item/DuplicatedItemValidator.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/item/ItemCommandService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/item/NewItemParam.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/item/UpdateItemParam.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/user/DuplicatedEmailConstraint.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/user/DuplicatedEmailValidator.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/user/DuplicatedUsernameConstraint.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/user/DuplicatedUsernameValidator.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/user/RegisterParam.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/user/UpdateUserCommand.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/user/UpdateUserParam.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/application/user/UserService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/comment/Comment.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/comment/CommentRepository.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/favorite/ItemFavorite.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/favorite/ItemFavoriteRepository.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/item/Item.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/item/ItemRepository.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/item/Tag.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/service/AuthorizationService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/service/JwtService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/user/FollowRelation.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/user/User.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/core/user/UserRepository.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/DateTimeHandler.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/CommentMapper.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/ItemFavoriteMapper.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/ItemMapper.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/UserMapper.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/ItemFavoritesReadService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/ItemReadService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/TagReadService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/UserReadService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/UserRelationshipQueryService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisCommentRepository.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisItemFavoriteRepository.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisItemRepository.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisUserRepository.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/service/DefaultJwtService.java delete mode 100644 .framework/java/backend/src/main/java/io/spring/infrastructure/service/SendEventService.java delete mode 100644 .framework/java/backend/src/main/resources/application-test.properties delete mode 100644 .framework/java/backend/src/main/resources/application.properties delete mode 100644 .framework/java/backend/src/main/resources/db/migration/V1__create_tables.sql delete mode 100644 .framework/java/backend/src/main/resources/mapper/CommentMapper.xml delete mode 100644 .framework/java/backend/src/main/resources/mapper/CommentReadService.xml delete mode 100644 .framework/java/backend/src/main/resources/mapper/ItemFavoriteMapper.xml delete mode 100644 .framework/java/backend/src/main/resources/mapper/ItemFavoritesReadService.xml delete mode 100644 .framework/java/backend/src/main/resources/mapper/ItemMapper.xml delete mode 100644 .framework/java/backend/src/main/resources/mapper/ItemReadService.xml delete mode 100644 .framework/java/backend/src/main/resources/mapper/TagReadService.xml delete mode 100644 .framework/java/backend/src/main/resources/mapper/TransferData.xml delete mode 100644 .framework/java/backend/src/main/resources/mapper/UserMapper.xml delete mode 100644 .framework/java/backend/src/main/resources/mapper/UserReadService.xml delete mode 100644 .framework/java/backend/src/main/resources/mapper/UserRelationshipQueryService.xml delete mode 100644 .framework/java/backend/src/test/java/io/spring/AnythinkMarketApplicationTests.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/TestHelper.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/api/CommentsApiTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/api/CurrentUserApiTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/api/ItemApiTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/api/ItemFavoriteApiTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/api/ItemsApiTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/api/ListItemApiTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/api/ProfileApiTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/api/TestWithCurrentUser.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/api/UsersApiTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/application/item/ItemQueryServiceTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/application/profile/ProfileQueryServiceTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/core/item/ItemTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/infrastructure/DbTestBase.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/infrastructure/favorite/MyBatisItemFavoriteRepositoryTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/infrastructure/item/ItemRepositoryTransactionTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/infrastructure/item/MyBatisItemRepositoryTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/infrastructure/service/DefaultJwtServiceTest.java delete mode 100644 .framework/java/backend/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java delete mode 100755 .framework/java/backend/start.sh delete mode 100644 .framework/java/charts/templates/anythink-backend-deployment.yaml delete mode 100644 .framework/java/charts/templates/database-deployment.yaml delete mode 100644 .framework/java/charts/values.yaml delete mode 100644 .framework/java/docker-compose.yml delete mode 100644 .framework/node/.devcontainer/devcontainer.json delete mode 100644 .framework/node/charts/.helmignore delete mode 100644 .framework/node/charts/Chart.yaml delete mode 100644 .framework/node/charts/templates/_helpers.yml delete mode 100644 .framework/node/charts/templates/anythink-backend-service.yaml delete mode 100644 .framework/node/charts/templates/anythink-frontend-deployment.yaml delete mode 100644 .framework/node/charts/templates/anythink-frontend-service.yaml delete mode 100644 .framework/node/charts/templates/database-pvc.yaml delete mode 100644 .framework/node/charts/templates/database-service.yaml delete mode 100644 .framework/python/.devcontainer/devcontainer.json delete mode 100644 .framework/python/backend/.dockerignore delete mode 100644 .framework/python/backend/.gitignore delete mode 100644 .framework/python/backend/Dockerfile.aws delete mode 100644 .framework/python/backend/LICENSE delete mode 100644 .framework/python/backend/README.md delete mode 100644 .framework/python/backend/alembic.ini delete mode 100644 .framework/python/backend/app/__init__.py delete mode 100644 .framework/python/backend/app/api/__init__.py delete mode 100644 .framework/python/backend/app/api/dependencies/__init__.py delete mode 100644 .framework/python/backend/app/api/dependencies/authentication.py delete mode 100644 .framework/python/backend/app/api/dependencies/comments.py delete mode 100644 .framework/python/backend/app/api/dependencies/database.py delete mode 100644 .framework/python/backend/app/api/dependencies/items.py delete mode 100644 .framework/python/backend/app/api/dependencies/profiles.py delete mode 100644 .framework/python/backend/app/api/errors/__init__.py delete mode 100644 .framework/python/backend/app/api/errors/http_error.py delete mode 100644 .framework/python/backend/app/api/errors/validation_error.py delete mode 100644 .framework/python/backend/app/api/routes/__init__.py delete mode 100644 .framework/python/backend/app/api/routes/api.py delete mode 100644 .framework/python/backend/app/api/routes/authentication.py delete mode 100644 .framework/python/backend/app/api/routes/comments.py delete mode 100644 .framework/python/backend/app/api/routes/home.py delete mode 100644 .framework/python/backend/app/api/routes/items/__init__.py delete mode 100644 .framework/python/backend/app/api/routes/items/api.py delete mode 100644 .framework/python/backend/app/api/routes/items/items_common.py delete mode 100644 .framework/python/backend/app/api/routes/items/items_resource.py delete mode 100644 .framework/python/backend/app/api/routes/ping.py delete mode 100644 .framework/python/backend/app/api/routes/profiles.py delete mode 100644 .framework/python/backend/app/api/routes/tags.py delete mode 100644 .framework/python/backend/app/api/routes/users.py delete mode 100644 .framework/python/backend/app/core/__init__.py delete mode 100644 .framework/python/backend/app/core/config.py delete mode 100644 .framework/python/backend/app/core/events.py delete mode 100644 .framework/python/backend/app/core/logging.py delete mode 100644 .framework/python/backend/app/core/settings/__init__.py delete mode 100644 .framework/python/backend/app/core/settings/app.py delete mode 100644 .framework/python/backend/app/core/settings/base.py delete mode 100644 .framework/python/backend/app/core/settings/development.py delete mode 100644 .framework/python/backend/app/core/settings/production.py delete mode 100644 .framework/python/backend/app/core/settings/test.py delete mode 100644 .framework/python/backend/app/db/__init__.py delete mode 100644 .framework/python/backend/app/db/errors.py delete mode 100644 .framework/python/backend/app/db/events.py delete mode 100644 .framework/python/backend/app/db/migrations/env.py delete mode 100644 .framework/python/backend/app/db/migrations/script.py.mako delete mode 100644 .framework/python/backend/app/db/migrations/versions/fdf8821871d7_main_tables.py delete mode 100644 .framework/python/backend/app/db/queries/__init__.py delete mode 100644 .framework/python/backend/app/db/queries/queries.py delete mode 100644 .framework/python/backend/app/db/queries/queries.pyi delete mode 100644 .framework/python/backend/app/db/queries/sql/comments.sql delete mode 100644 .framework/python/backend/app/db/queries/sql/items.sql delete mode 100644 .framework/python/backend/app/db/queries/sql/profiles.sql delete mode 100644 .framework/python/backend/app/db/queries/sql/tags.sql delete mode 100644 .framework/python/backend/app/db/queries/sql/users.sql delete mode 100644 .framework/python/backend/app/db/queries/tables.py delete mode 100644 .framework/python/backend/app/db/repositories/__init__.py delete mode 100644 .framework/python/backend/app/db/repositories/base.py delete mode 100644 .framework/python/backend/app/db/repositories/comments.py delete mode 100644 .framework/python/backend/app/db/repositories/items.py delete mode 100644 .framework/python/backend/app/db/repositories/profiles.py delete mode 100644 .framework/python/backend/app/db/repositories/tags.py delete mode 100644 .framework/python/backend/app/db/repositories/users.py delete mode 100644 .framework/python/backend/app/db/seeds.py delete mode 100644 .framework/python/backend/app/main.py delete mode 100644 .framework/python/backend/app/models/__init__.py delete mode 100644 .framework/python/backend/app/models/common.py delete mode 100644 .framework/python/backend/app/models/domain/__init__.py delete mode 100644 .framework/python/backend/app/models/domain/comments.py delete mode 100644 .framework/python/backend/app/models/domain/items.py delete mode 100644 .framework/python/backend/app/models/domain/profiles.py delete mode 100644 .framework/python/backend/app/models/domain/rwmodel.py delete mode 100644 .framework/python/backend/app/models/domain/users.py delete mode 100644 .framework/python/backend/app/models/schemas/__init__.py delete mode 100644 .framework/python/backend/app/models/schemas/comments.py delete mode 100644 .framework/python/backend/app/models/schemas/items.py delete mode 100644 .framework/python/backend/app/models/schemas/jwt.py delete mode 100644 .framework/python/backend/app/models/schemas/profiles.py delete mode 100644 .framework/python/backend/app/models/schemas/rwschema.py delete mode 100644 .framework/python/backend/app/models/schemas/tags.py delete mode 100644 .framework/python/backend/app/models/schemas/users.py delete mode 100644 .framework/python/backend/app/resources/__init__.py delete mode 100644 .framework/python/backend/app/resources/strings.py delete mode 100644 .framework/python/backend/app/services/__init__.py delete mode 100644 .framework/python/backend/app/services/authentication.py delete mode 100644 .framework/python/backend/app/services/event.py delete mode 100644 .framework/python/backend/app/services/items.py delete mode 100644 .framework/python/backend/app/services/jwt.py delete mode 100644 .framework/python/backend/app/services/security.py delete mode 100644 .framework/python/backend/poetry.lock delete mode 100644 .framework/python/backend/pyproject.toml delete mode 100644 .framework/python/backend/requirements.txt delete mode 100644 .framework/python/backend/runtime.txt delete mode 100755 .framework/python/backend/scripts/format delete mode 100755 .framework/python/backend/scripts/heroku_release.sh delete mode 100755 .framework/python/backend/scripts/lint delete mode 100755 .framework/python/backend/scripts/test delete mode 100755 .framework/python/backend/scripts/test-cov-html delete mode 100755 .framework/python/backend/seeds.sh delete mode 100644 .framework/python/backend/setup.cfg delete mode 100644 .framework/python/charts/.helmignore delete mode 100644 .framework/python/charts/Chart.yaml delete mode 100644 .framework/python/charts/templates/_helpers.yml delete mode 100644 .framework/python/charts/templates/anythink-backend-deployment.yaml delete mode 100644 .framework/python/charts/templates/anythink-backend-service.yaml delete mode 100644 .framework/python/charts/templates/anythink-frontend-deployment.yaml delete mode 100644 .framework/python/charts/templates/anythink-frontend-service.yaml delete mode 100644 .framework/python/charts/templates/database-deployment.yaml delete mode 100644 .framework/python/charts/templates/database-pvc.yaml delete mode 100644 .framework/python/charts/templates/database-service.yaml delete mode 100644 .framework/python/charts/values.yaml delete mode 100644 .framework/python/docker-compose.yml delete mode 100644 .framework/rails/.devcontainer/devcontainer.json delete mode 100644 .framework/rails/backend/.gitignore delete mode 100644 .framework/rails/backend/.ruby-version delete mode 100644 .framework/rails/backend/Dockerfile.aws delete mode 100644 .framework/rails/backend/Gemfile delete mode 100644 .framework/rails/backend/Gemfile.lock delete mode 100644 .framework/rails/backend/README.md delete mode 100644 .framework/rails/backend/Rakefile delete mode 100644 .framework/rails/backend/app/assets/config/manifest.js delete mode 100644 .framework/rails/backend/app/assets/images/.keep delete mode 100644 .framework/rails/backend/app/assets/javascripts/.keep delete mode 100644 .framework/rails/backend/app/assets/stylesheets/.keep delete mode 100644 .framework/rails/backend/app/channels/application_cable/channel.rb delete mode 100644 .framework/rails/backend/app/channels/application_cable/connection.rb delete mode 100644 .framework/rails/backend/app/controllers/application_controller.rb delete mode 100644 .framework/rails/backend/app/controllers/comments_controller.rb delete mode 100644 .framework/rails/backend/app/controllers/concerns/.keep delete mode 100644 .framework/rails/backend/app/controllers/favorites_controller.rb delete mode 100644 .framework/rails/backend/app/controllers/follows_controller.rb delete mode 100644 .framework/rails/backend/app/controllers/items_controller.rb delete mode 100644 .framework/rails/backend/app/controllers/ping_controller.rb delete mode 100644 .framework/rails/backend/app/controllers/profiles_controller.rb delete mode 100644 .framework/rails/backend/app/controllers/registrations_controller.rb delete mode 100644 .framework/rails/backend/app/controllers/sessions_controller.rb delete mode 100644 .framework/rails/backend/app/controllers/tags_controller.rb delete mode 100644 .framework/rails/backend/app/controllers/users_controller.rb delete mode 100644 .framework/rails/backend/app/helpers/application_helper.rb delete mode 100644 .framework/rails/backend/app/jobs/application_job.rb delete mode 100644 .framework/rails/backend/app/mailers/application_mailer.rb delete mode 100644 .framework/rails/backend/app/models/application_record.rb delete mode 100644 .framework/rails/backend/app/models/comment.rb delete mode 100644 .framework/rails/backend/app/models/concerns/.keep delete mode 100644 .framework/rails/backend/app/models/favorite.rb delete mode 100644 .framework/rails/backend/app/models/follow.rb delete mode 100644 .framework/rails/backend/app/models/item.rb delete mode 100644 .framework/rails/backend/app/models/user.rb delete mode 100644 .framework/rails/backend/app/views/comments/_comment.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/comments/create.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/comments/index.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/devise/registrations/create.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/devise/sessions/create.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/items/_item.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/items/index.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/items/show.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/profiles/_profile.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/profiles/show.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/users/_user.json.jbuilder delete mode 100644 .framework/rails/backend/app/views/users/show.json.jbuilder delete mode 100755 .framework/rails/backend/bin/bundle delete mode 100755 .framework/rails/backend/bin/heroku_release.sh delete mode 100755 .framework/rails/backend/bin/rails delete mode 100755 .framework/rails/backend/bin/rake delete mode 100755 .framework/rails/backend/bin/setup delete mode 100755 .framework/rails/backend/bin/spring delete mode 100755 .framework/rails/backend/bin/update delete mode 100755 .framework/rails/backend/bin/yarn delete mode 100644 .framework/rails/backend/config.ru delete mode 100644 .framework/rails/backend/config/application.rb delete mode 100644 .framework/rails/backend/config/boot.rb delete mode 100644 .framework/rails/backend/config/cable.yml delete mode 100644 .framework/rails/backend/config/credentials.yml.enc delete mode 100644 .framework/rails/backend/config/database.yml delete mode 100644 .framework/rails/backend/config/environment.rb delete mode 100644 .framework/rails/backend/config/environments/development.rb delete mode 100644 .framework/rails/backend/config/environments/production.rb delete mode 100644 .framework/rails/backend/config/environments/test.rb delete mode 100644 .framework/rails/backend/config/initializers/application_controller_renderer.rb delete mode 100644 .framework/rails/backend/config/initializers/assets.rb delete mode 100644 .framework/rails/backend/config/initializers/backtrace_silencers.rb delete mode 100644 .framework/rails/backend/config/initializers/content_security_policy.rb delete mode 100644 .framework/rails/backend/config/initializers/cookies_serializer.rb delete mode 100644 .framework/rails/backend/config/initializers/cors.rb delete mode 100644 .framework/rails/backend/config/initializers/devise.rb delete mode 100644 .framework/rails/backend/config/initializers/filter_parameter_logging.rb delete mode 100644 .framework/rails/backend/config/initializers/inflections.rb delete mode 100644 .framework/rails/backend/config/initializers/json_param_key_transform.rb delete mode 100644 .framework/rails/backend/config/initializers/mime_types.rb delete mode 100644 .framework/rails/backend/config/initializers/wrap_parameters.rb delete mode 100644 .framework/rails/backend/config/locales/devise.en.yml delete mode 100644 .framework/rails/backend/config/locales/en.yml delete mode 100644 .framework/rails/backend/config/locales/responders.en.yml delete mode 100644 .framework/rails/backend/config/puma.rb delete mode 100644 .framework/rails/backend/config/routes.rb delete mode 100644 .framework/rails/backend/config/secrets.yml delete mode 100644 .framework/rails/backend/config/secrets.yml.enc delete mode 100644 .framework/rails/backend/config/spring.rb delete mode 100644 .framework/rails/backend/config/storage.yml delete mode 100644 .framework/rails/backend/db/migrate/20210412045707_devise_create_users.rb delete mode 100644 .framework/rails/backend/db/migrate/20210412045739_add_profile_fields_to_users.rb delete mode 100644 .framework/rails/backend/db/migrate/20210412052128_create_items.rb delete mode 100644 .framework/rails/backend/db/migrate/20210412054809_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb delete mode 100644 .framework/rails/backend/db/migrate/20210412054810_add_missing_unique_indices.acts_as_taggable_on_engine.rb delete mode 100644 .framework/rails/backend/db/migrate/20210412054811_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb delete mode 100644 .framework/rails/backend/db/migrate/20210412054812_add_missing_taggable_index.acts_as_taggable_on_engine.rb delete mode 100644 .framework/rails/backend/db/migrate/20210412054813_change_collation_for_tag_names.acts_as_taggable_on_engine.rb delete mode 100644 .framework/rails/backend/db/migrate/20210412055201_create_favorites.rb delete mode 100644 .framework/rails/backend/db/migrate/20210412061113_create_comments.rb delete mode 100644 .framework/rails/backend/db/migrate/20210412061614_acts_as_follower_migration.rb delete mode 100644 .framework/rails/backend/db/schema.rb delete mode 100644 .framework/rails/backend/db/seeds.rb delete mode 100644 .framework/rails/backend/lib/assets/.keep delete mode 100644 .framework/rails/backend/lib/event.rb delete mode 100644 .framework/rails/backend/lib/tasks/.keep delete mode 100644 .framework/rails/backend/log/.keep delete mode 100644 .framework/rails/backend/public/404.html delete mode 100644 .framework/rails/backend/public/422.html delete mode 100644 .framework/rails/backend/public/500.html delete mode 100644 .framework/rails/backend/public/favicon.ico delete mode 100644 .framework/rails/backend/public/robots.txt delete mode 100755 .framework/rails/backend/seeds.sh delete mode 100755 .framework/rails/backend/start.sh delete mode 100755 .framework/rails/backend/start_rails.sh delete mode 100644 .framework/rails/backend/vendor/.keep delete mode 100644 .framework/rails/charts/.helmignore delete mode 100644 .framework/rails/charts/Chart.yaml delete mode 100644 .framework/rails/charts/templates/_helpers.yml delete mode 100644 .framework/rails/charts/templates/anythink-backend-deployment.yaml delete mode 100644 .framework/rails/charts/templates/anythink-backend-service.yaml delete mode 100644 .framework/rails/charts/templates/anythink-frontend-deployment.yaml delete mode 100644 .framework/rails/charts/templates/anythink-frontend-service.yaml delete mode 100644 .framework/rails/charts/templates/database-deployment.yaml delete mode 100644 .framework/rails/charts/templates/database-pvc.yaml delete mode 100644 .framework/rails/charts/templates/database-service.yaml delete mode 100644 .framework/rails/charts/values.yaml delete mode 100644 .framework/rails/docker-compose.yml rename .github/{.workflows => workflows}/k8s.yml (100%) delete mode 100644 .github/workflows/test_e2e_java.yml delete mode 100644 .github/workflows/test_e2e_lint.yml delete mode 100644 .github/workflows/test_e2e_node.yml delete mode 100644 .github/workflows/test_e2e_python.yml delete mode 100644 .github/workflows/test_e2e_rails.yml delete mode 100644 .github/workflows/test_frontend_react.yml create mode 100644 .github/workflows/wilco-actions.yml create mode 100644 .wilco rename {.framework/node/backend => backend}/.gitignore (100%) create mode 100644 backend/Dockerfile rename {.framework/node/backend => backend}/Dockerfile.aws (100%) rename {.framework/node/backend => backend}/README.md (100%) rename {.framework/node/backend => backend}/app.js (98%) rename {.framework/node/backend => backend}/config/index.js (78%) rename {.framework/node/backend => backend}/config/passport.js (100%) rename {.framework/node/backend => backend}/lib/event.js (100%) rename {.framework/node/backend => backend}/models/Comment.js (100%) rename {.framework/node/backend => backend}/models/Item.js (100%) rename {.framework/node/backend => backend}/models/User.js (100%) rename {.framework/node/backend => backend}/package.json (100%) rename {.framework/node/backend => backend}/public/.keep (100%) create mode 100644 backend/routes/api/comments.js rename {.framework/node/backend => backend}/routes/api/index.js (92%) rename {.framework/node/backend => backend}/routes/api/items.js (100%) rename {.framework/node/backend => backend}/routes/api/ping.js (100%) rename {.framework/node/backend => backend}/routes/api/profiles.js (100%) rename {.framework/node/backend => backend}/routes/api/tags.js (100%) rename {.framework/node/backend => backend}/routes/api/users.js (100%) rename {.framework/node/backend => backend}/routes/auth.js (100%) rename {.framework/node/backend => backend}/routes/index.js (100%) rename {.framework/node/backend => backend}/scripts/seeds.js (100%) rename {.framework/node/backend => backend}/seeds.sh (100%) rename {.framework/node/backend => backend}/start.sh (100%) rename {.framework/node/backend => backend}/tests/api-tests.postman.json (100%) rename {.framework/node/backend => backend}/tests/env-api-tests.postman.json (100%) rename {.framework/node/backend => backend}/yarn.lock (100%) rename {.framework/java/charts => charts}/.helmignore (100%) rename {.framework/java/charts => charts}/Chart.yaml (100%) rename {.framework/java/charts => charts}/templates/_helpers.yml (100%) rename {.framework/node/charts => charts}/templates/anythink-backend-deployment.yaml (100%) rename {.framework/java/charts => charts}/templates/anythink-backend-service.yaml (100%) rename {.framework/java/charts => charts}/templates/anythink-frontend-deployment.yaml (100%) rename {.framework/java/charts => charts}/templates/anythink-frontend-service.yaml (100%) rename {.framework/node/charts => charts}/templates/database-deployment.yaml (100%) rename {.framework/java/charts => charts}/templates/database-pvc.yaml (100%) rename {.framework/java/charts => charts}/templates/database-service.yaml (100%) rename {.framework/node/charts => charts}/values.yaml (100%) rename .framework/node/docker-compose.yml => docker-compose.yml (75%) rename {.framework/react/frontend => frontend}/.eslintignore (100%) rename {.framework/react/frontend => frontend}/.gitignore (100%) create mode 100644 frontend/Dockerfile rename {.framework/react/frontend => frontend}/Dockerfile.aws (100%) rename {.framework/react/frontend => frontend}/jest.config.js (100%) rename {.framework/react/frontend => frontend}/package.json (100%) rename {.framework/react/frontend => frontend}/public/50precentoff.png (100%) rename {.framework/react/frontend => frontend}/public/favicon.ico (100%) rename {.framework/react/frontend => frontend}/public/index.html (100%) rename {.framework/react/frontend => frontend}/public/placeholder.png (100%) rename {.framework/react/frontend => frontend}/public/style.css (100%) rename {.framework/react/frontend => frontend}/public/sunray.jpeg (100%) rename {.framework/react/frontend => frontend}/public/verified_seller.svg (100%) rename {.framework/react/frontend => frontend}/readme.md (100%) rename {.framework/react/frontend => frontend}/src/agent.js (100%) rename {.framework/react/frontend => frontend}/src/components/App.js (100%) rename {.framework/react/frontend => frontend}/src/components/Editor.js (100%) rename {.framework/react/frontend => frontend}/src/components/Header.js (100%) rename {.framework/react/frontend => frontend}/src/components/Home/Banner.js (100%) rename {.framework/react/frontend => frontend}/src/components/Home/MainView.js (100%) rename {.framework/react/frontend => frontend}/src/components/Home/Tags.js (100%) rename {.framework/react/frontend => frontend}/src/components/Home/index.js (100%) rename {.framework/react/frontend => frontend}/src/components/Item/Comment.js (100%) rename {.framework/react/frontend => frontend}/src/components/Item/CommentContainer.js (100%) rename {.framework/react/frontend => frontend}/src/components/Item/CommentInput.js (100%) rename {.framework/react/frontend => frontend}/src/components/Item/CommentList.js (100%) rename {.framework/react/frontend => frontend}/src/components/Item/DeleteButton.js (100%) rename {.framework/react/frontend => frontend}/src/components/Item/ItemActions.js (100%) rename {.framework/react/frontend => frontend}/src/components/Item/ItemMeta.js (100%) rename {.framework/react/frontend => frontend}/src/components/Item/index.js (100%) rename {.framework/react/frontend => frontend}/src/components/Item/utils/ItemFetcher.js (100%) rename {.framework/react/frontend => frontend}/src/components/ItemList.js (100%) rename {.framework/react/frontend => frontend}/src/components/ItemPreview.js (100%) rename {.framework/react/frontend => frontend}/src/components/ListErrors.js (100%) rename {.framework/react/frontend => frontend}/src/components/ListPagination.js (100%) rename {.framework/react/frontend => frontend}/src/components/Login.js (100%) rename {.framework/react/frontend => frontend}/src/components/Profile.js (100%) rename {.framework/react/frontend => frontend}/src/components/ProfileFavorites.js (100%) rename {.framework/react/frontend => frontend}/src/components/Register.js (100%) rename {.framework/react/frontend => frontend}/src/components/Settings.js (100%) rename {.framework/react/frontend => frontend}/src/components/commons.js (100%) rename {.framework/react/frontend => frontend}/src/constants/actionTypes.js (100%) rename {.framework/react/frontend => frontend}/src/custom.scss (100%) rename {.framework/react/frontend => frontend}/src/imgs/background.png (100%) rename {.framework/react/frontend => frontend}/src/imgs/logo.png (100%) rename {.framework/react/frontend => frontend}/src/imgs/topbar_logo.png (100%) rename {.framework/react/frontend => frontend}/src/index.js (100%) rename {.framework/react/frontend => frontend}/src/middleware.js (100%) rename {.framework/react/frontend => frontend}/src/reducer.js (100%) rename {.framework/react/frontend => frontend}/src/reducers/auth.js (100%) rename {.framework/react/frontend => frontend}/src/reducers/common.js (100%) rename {.framework/react/frontend => frontend}/src/reducers/editor.js (100%) rename {.framework/react/frontend => frontend}/src/reducers/home.js (100%) rename {.framework/react/frontend => frontend}/src/reducers/item.js (100%) rename {.framework/react/frontend => frontend}/src/reducers/itemList.js (100%) rename {.framework/react/frontend => frontend}/src/reducers/profile.js (100%) rename {.framework/react/frontend => frontend}/src/reducers/settings.js (100%) rename {.framework/react/frontend => frontend}/src/setupTests.js (100%) rename {.framework/react/frontend => frontend}/src/store.js (100%) rename {.framework/react/frontend => frontend}/src/tests/components/Header.test.js (100%) rename {.framework/react/frontend => frontend}/src/tests/components/__snapshots__/Header.test.js.snap (100%) rename {.framework/react/frontend => frontend}/src/tests/item/CommentInput.test.js (100%) rename {.framework/react/frontend => frontend}/src/tests/item/__snapshots__/CommentInput.test.js.snap (100%) rename {.framework/react/frontend => frontend}/start.sh (100%) rename {.framework/react/frontend => frontend}/yarn.lock (100%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..930afce --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,9 @@ +{ + "name": "Anythink Development Container", + "image": "public.ecr.aws/v0a2l7y2/wilco/anythink-devcontainer:latest", + "customizations": { + "vscode": { + "extensions": ["GitHub.copilot", "GitHub.copilot-chat"] + } + } +} diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh index f7bc939..5a6cfc3 100755 --- a/.devcontainer/setup.sh +++ b/.devcontainer/setup.sh @@ -15,10 +15,4 @@ echo "export CODESPACE_WDS_SOCKET_PORT=443" >> ~/.bashrc echo "printf \"\n\n☁️☁️☁️️ Anythink: Develop in the Cloud ☁️☁️☁️\n\"" >> ~/.bashrc echo "printf \"\n\x1b[31m \x1b[1m👉 Type: \\\`docker compose up\\\` to run the project. 👈\n\n\"" >> ~/.bashrc -nohup bash -c "cd /wilco-agent && node agent.js &" >> /tmp/agent.log 2>&1 - -# Check if docker is installed -if command -v docker &> /dev/null -then - docker compose pull -fi +nohup bash -c "cd /wilco-agent && node agent.js &" >> /tmp/agent.log 2>&1 diff --git a/.framework/java/.devcontainer/devcontainer.json b/.framework/java/.devcontainer/devcontainer.json deleted file mode 100644 index 5e6dee5..0000000 --- a/.framework/java/.devcontainer/devcontainer.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Anythink Development Container", - "image": "public.ecr.aws/v0a2l7y2/wilco/anythink-devcontainer-java:latest" -} diff --git a/.framework/java/backend/.gitignore b/.framework/java/backend/.gitignore deleted file mode 100644 index dfcbb12..0000000 --- a/.framework/java/backend/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -.gradle/ -/build/ -!gradle/wrapper/gradle-wrapper.jar -*.db - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -nbproject/private/ -build/ -nbbuild/ -dist/ -nbdist/ -.nb-gradle/ diff --git a/.framework/java/backend/Dockerfile.aws b/.framework/java/backend/Dockerfile.aws deleted file mode 100644 index e15ec67..0000000 --- a/.framework/java/backend/Dockerfile.aws +++ /dev/null @@ -1,9 +0,0 @@ -FROM openjdk:21 - -WORKDIR /usr/src -COPY backend ./backend -COPY .wilco ./.wilco - -# Pre-install packages -WORKDIR /usr/src/backend -RUN ./gradlew build diff --git a/.framework/java/backend/LICENSE b/.framework/java/backend/LICENSE deleted file mode 100644 index a6fd2b7..0000000 --- a/.framework/java/backend/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Aisensiy - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.framework/java/backend/README.md b/.framework/java/backend/README.md deleted file mode 100644 index 52e4265..0000000 --- a/.framework/java/backend/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Anythink Market Backend - -# How it works - -The application uses Spring Boot (Web, Mybatis). - -And the code is organized as this: - -1. `api` is the web layer implemented by Spring MVC -2. `core` is the business model including entities and services -3. `application` is the high-level services for querying the data transfer objects -4. `infrastructure` contains all the implementation classes as the technique details - -# Getting started - -You'll need Java 21 installed. - - ./gradlew bootRun - -To test that it works, open a browser tab at http://localhost:3000/api/tags -Alternatively, you can run: - - curl http://localhost:3000/api/tags - -# Run test - -The repository contains a lot of test cases to cover both api test and repository test. - - ./gradlew test - -# Code format - -Use spotless for code format. - - ./gradlew spotlessJavaApply diff --git a/.framework/java/backend/build.gradle b/.framework/java/backend/build.gradle deleted file mode 100644 index 2f04f67..0000000 --- a/.framework/java/backend/build.gradle +++ /dev/null @@ -1,79 +0,0 @@ -plugins { - id 'org.springframework.boot' version '3.2.2' - id 'io.spring.dependency-management' version '1.0.11.RELEASE' - id 'java' - id "com.diffplug.spotless" version "6.25.0" -} - -version = '0.0.1-SNAPSHOT' -sourceCompatibility = '21' -targetCompatibility = '21' - -spotless { - java { - target project.fileTree(project.rootDir) { - include '**/*.java' - exclude 'build/generated/**/*.*', 'build/generated-examples/**/*.*' - } - googleJavaFormat() - } -} - -repositories { - mavenCentral() -} - -configurations { - compileOnly { - extendsFrom annotationProcessor - } -} - -tasks.named("spotlessJava").configure { dependsOn("processResources") } -tasks.named("spotlessJava").configure { dependsOn("compileJava") } -tasks.named("spotlessJava").configure { dependsOn("compileTestJava") } -spotlessJava.dependsOn test - -dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'jakarta.validation:jakarta.validation-api:3.0.2' - implementation 'org.springframework.boot:spring-boot-starter-hateoas' - implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.boot:spring-boot-starter-actuator' - implementation 'org.springframework.boot:spring-boot-devtools' - implementation 'org.springframework.security:spring-security-web' - implementation 'org.springframework.security:spring-security-config' - implementation 'com.squareup.okhttp3:okhttp:4.9.1' - implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3' - implementation 'org.flywaydb:flyway-core' - implementation 'io.jsonwebtoken:jjwt-api:0.11.2' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.2', - 'io.jsonwebtoken:jjwt-jackson:0.11.2' - implementation 'joda-time:joda-time:2.10.13' - implementation 'org.xerial:sqlite-jdbc:3.36.0.3' - implementation 'org.postgresql:postgresql:42.2.24' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.16.1' - - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' - - testImplementation 'io.rest-assured:rest-assured:5.4.0' - testImplementation 'io.rest-assured:json-path:5.4.0' - testImplementation 'io.rest-assured:xml-path:5.4.0' - testImplementation 'io.rest-assured:spring-mock-mvc:5.4.0' - testImplementation 'org.springframework.security:spring-security-test' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3' - testImplementation 'com.h2database:h2:1.4.200' -} - -tasks.named('test') { - useJUnitPlatform() -} - -tasks.named('clean') { - doFirst { - delete './dev.db' - } -} diff --git a/.framework/java/backend/gradle/wrapper/gradle-wrapper.jar b/.framework/java/backend/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59203 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

K7UpUK$>l@ zw`1S}(D?mUXu_C{wupRS-jiX~w=Uqqhf|Vb3Cm9L=T+w91Cu^ z*&Ty%sN?x*h~mJc4g~k{xD4ZmF%FXZNC;oVDwLZ_WvrnzY|{v8hc1nmx4^}Z;yriXsAf+Lp+OFLbR!&Ox?xABwl zu8w&|5pCxmu#$?Cv2_-Vghl2LZ6m7}VLEfR5o2Ou$x02uA-%QB2$c(c1rH3R9hesc zfpn#oqpbKuVsdfV#cv@5pV4^f_!WS+F>SV6N0JQ9E!T90EX((_{bSSFv9ld%I0&}9 zH&Jd4MEX1e0iqDtq~h?DBrxQX1iI0lIs<|kB$Yrh&cpeK0-^K%=FBsCBT46@h#yi!AyDq1V(#V}^;{{V*@T4WJ&U-NTq43w=|K>z8%pr_nC>%C(Wa_l78Ufib$r8Od)IIN=u>417 z`Hl{9A$mI5A(;+-Q&$F&h-@;NR>Z<2U;Y21>>Z;s@0V@SbkMQQj%_;~+qTuQ?c|AV zcWm3XZQHhP&R%QWarS%mJ!9R^&!_)*s(v+VR@I#QrAT}`17Y+l<`b-nvmDNW`De%y zrwTZ9EJrj1AFA>B`1jYDow}~*dfPs}IZMO3=a{Fy#IOILc8F0;JS4x(k-NSpbN@qM z`@aE_e}5{!$v3+qVs7u?sOV(y@1Os*Fgu`fCW9=G@F_#VQ%xf$hj0~wnnP0$hFI+@ zkQj~v#V>xn)u??YutKsX>pxKCl^p!C-o?+9;!Nug^ z{rP!|+KsP5%uF;ZCa5F;O^9TGac=M|=V z_H(PfkV1rz4jl?gJ(ArXMyWT4y(86d3`$iI4^l9`vLdZkzpznSd5Ikfrs8qcSy&>z zTIZgWZGXw0n9ibQxYWE@gI0(3#KA-dAdPcsL_|hg2@~C!VZDM}5;v_Nykfq!*@*Zf zE_wVgx82GMDryKO{U{D>vSzSc%B~|cjDQrt5BN=Ugpsf8H8f1lR4SGo#hCuXPL;QQ z#~b?C4MoepT3X`qdW2dNn& zo8)K}%Lpu>0tQei+{>*VGErz|qjbK#9 zvtd8rcHplw%YyQCKR{kyo6fgg!)6tHUYT(L>B7er5)41iG`j$qe*kSh$fY!PehLcD zWeKZHn<492B34*JUQh=CY1R~jT9Jt=k=jCU2=SL&&y5QI2uAG2?L8qd2U(^AW#{(x zThSy=C#>k+QMo^7caQcpU?Qn}j-`s?1vXuzG#j8(A+RUAY})F@=r&F(8nI&HspAy4 z4>(M>hI9c7?DCW8rw6|23?qQMSq?*Vx?v30U%luBo)B-k2mkL)Ljk5xUha3pK>EEj z@(;tH|M@xkuN?gsz;*bygizwYR!6=(Xgcg^>WlGtRYCozY<rFX2E>kaZo)O<^J7a`MX8Pf`gBd4vrtD|qKn&B)C&wp0O-x*@-|m*0egT=-t@%dD zgP2D+#WPptnc;_ugD6%zN}Z+X4=c61XNLb7L1gWd8;NHrBXwJ7s0ce#lWnnFUMTR& z1_R9Fin4!d17d4jpKcfh?MKRxxQk$@)*hradH2$3)nyXep5Z;B z?yX+-Bd=TqO2!11?MDtG0n(*T^!CIiF@ZQymqq1wPM_X$Iu9-P=^}v7npvvPBu!d$ z7K?@CsA8H38+zjA@{;{kG)#AHME>Ix<711_iQ@WWMObXyVO)a&^qE1GqpP47Q|_AG zP`(AD&r!V^MXQ^e+*n5~Lp9!B+#y3#f8J^5!iC@3Y@P`;FoUH{G*pj*q7MVV)29+j z>BC`a|1@U_v%%o9VH_HsSnM`jZ-&CDvbiqDg)tQEnV>b%Ptm)T|1?TrpIl)Y$LnG_ zzKi5j2Fx^K^PG1=*?GhK;$(UCF-tM~^=Z*+Wp{FSuy7iHt9#4n(sUuHK??@v+6*|10Csdnyg9hAsC5_OrSL;jVkLlf zHXIPukLqbhs~-*oa^gqgvtpgTk_7GypwH><53riYYL*M=Q@F-yEPLqQ&1Sc zZB%w}T~RO|#jFjMWcKMZccxm-SL)s_ig?OC?y_~gLFj{n8D$J_Kw%{r0oB8?@dWzn zB528d-wUBQzrrSSLq?fR!K%59Zv9J4yCQhhDGwhptpA5O5U?Hjqt>8nOD zi{)0CI|&Gu%zunGI*XFZh(ix)q${jT8wnnzbBMPYVJc4HX*9d^mz|21$=R$J$(y7V zo0dxdbX3N#=F$zjstTf*t8vL)2*{XH!+<2IJ1VVFa67|{?LP&P41h$2i2;?N~RA30LV`BsUcj zfO9#Pg1$t}7zpv#&)8`mis3~o+P(DxOMgz-V*(?wWaxi?R=NhtW}<#^Z?(BhSwyar zG|A#Q7wh4OfK<|DAcl9THc-W4*>J4nTevsD%dkj`U~wSUCh15?_N@uMdF^Kw+{agk zJ`im^wDqj`Ev)W3k3stasP`88-M0ZBs7;B6{-tSm3>I@_e-QfT?7|n0D~0RRqDb^G zyHb=is;IwuQ&ITzL4KsP@Z`b$d%B0Wuhioo1CWttW8yhsER1ZUZzA{F*K=wmi-sb#Ju+j z-l@In^IKnb{bQG}Ps>+Vu_W#grNKNGto+yjA)?>0?~X`4I3T@5G1)RqGUZuP^NJCq&^HykuYtMDD8qq+l8RcZNJsvN(10{ zQ1$XcGt}QH-U^WU!-wRR1d--{B$%vY{JLWIV%P4-KQuxxDeJaF#{eu&&r!3Qu{w}0f--8^H|KwE>)ORrcR+2Qf zb})DRcH>k0zWK8@{RX}NYvTF;E~phK{+F;MkIP$)T$93Ba2R2TvKc>`D??#mv9wg$ zd~|-`Qx5LwwsZ2hb*Rt4S9dsF%Cny5<1fscy~)d;0m2r$f=83<->c~!GNyb!U)PA; zq^!`@@)UaG)Ew(9V?5ZBq#c%dCWZrplmuM`o~TyHjAIMh0*#1{B>K4po-dx$Tk-Cq z=WZDkP5x2W&Os`N8KiYHRH#UY*n|nvd(U>yO=MFI-2BEp?x@=N<~CbLJBf6P)}vLS?xJXYJ2^<3KJUdrwKnJnTp{ zjIi|R=L7rn9b*D#Xxr4*R<3T5AuOS+#U8hNlfo&^9JO{VbH!v9^JbK=TCGR-5EWR@ zN8T-_I|&@A}(hKeL4_*eb!1G8p~&_Im8|wc>Cdir+gg90n1dw?QaXcx6Op_W1r=axRw>4;rM*UOpT#Eb9xU1IiWo@h?|5uP zka>-XW0Ikp@dIe;MN8B01a7+5V@h3WN{J=HJ*pe0uwQ3S&MyWFni47X32Q7SyCTNQ z+sR!_9IZa5!>f&V$`q!%H8ci!a|RMx5}5MA_kr+bhtQy{-^)(hCVa@I!^TV4RBi zAFa!Nsi3y37I5EK;0cqu|9MRj<^r&h1lF}u0KpKQD^5Y+LvFEwM zLU@@v4_Na#Axy6tn3P%sD^5P#<7F;sd$f4a7LBMk zGU^RZHBcxSA%kCx*eH&wgA?Qwazm8>9SCSz_!;MqY-QX<1@p$*T8lc?@`ikEqJ>#w zcG``^CoFMAhdEXT9qt47g0IZkaU)4R7wkGs^Ax}usqJ5HfDYAV$!=6?>J6+Ha1I<5 z|6=9soU4>E))tW$<#>F ziZ$6>KJf0bPfbx_)7-}tMINlc=}|H+$uX)mhC6-Hz+XZxsKd^b?RFB6et}O#+>Wmw9Ec9) z{q}XFWp{3@qmyK*Jvzpyqv57LIR;hPXKsrh{G?&dRjF%Zt5&m20Ll?OyfUYC3WRn{cgQ?^V~UAv+5 z&_m#&nIwffgX1*Z2#5^Kl4DbE#NrD&Hi4|7SPqZ}(>_+JMz=s|k77aEL}<=0Zfb)a z%F(*L3zCA<=xO)2U3B|pcTqDbBoFp>QyAEU(jMu8(jLA61-H!ucI804+B!$E^cQQa z)_ERrW3g!B9iLb3nn3dlkvD7KsY?sRvls3QC0qPi>o<)GHx%4Xb$5a3GBTJ(k@`e@ z$RUa^%S15^1oLEmA=sayrP5;9qtf!Z1*?e$ORVPsXpL{jL<6E)0sj&swP3}NPmR%FM?O>SQgN5XfHE< zo(4#Cv11(%Nnw_{_Ro}r6=gKd{k?NebJ~<~Kv0r(r0qe4n3LFx$5%x(BKvrz$m?LG zjLIc;hbj0FMdb9aH9Lpsof#yG$(0sG2%RL;d(n>;#jb!R_+dad+K;Ccw!|RY?uS(a zj~?=&M!4C(5LnlH6k%aYvz@7?xRa^2gml%vn&eKl$R_lJ+e|xsNfXzr#xuh(>`}9g zLHSyiFwK^-p!;p$yt7$F|3*IfO3Mlu9e>Dpx8O`37?fA`cj`C0B-m9uRhJjs^mRp# zWB;Aj6|G^1V6`jg7#7V9UFvnB4((nIwG?k%c7h`?0tS8J3Bn0t#pb#SA}N-|45$-j z$R>%7cc2ebAClXc(&0UtHX<>pd)akR3Kx_cK+n<}FhzmTx!8e9^u2e4%x{>T6pQ`6 zO182bh$-W5A3^wos0SV_TgPmF4WUP-+D25KjbC{y_6W_9I2_vNKwU(^qSdn&>^=*t z&uvp*@c8#2*paD!ZMCi3;K{Na;I4Q35zw$YrW5U@Kk~)&rw;G?d7Q&c9|x<Hg|CNMsxovmfth*|E*GHezPTWa^Hd^F4!B3sF;)? z(NaPyAhocu1jUe(!5Cy|dh|W2=!@fNmuNOzxi^tE_jAtzNJ0JR-avc_H|ve#KO}#S z#a(8secu|^Tx553d4r@3#6^MHbH)vmiBpn0X^29xEv!Vuh1n(Sr5I0V&`jA2;WS|Y zbf0e}X|)wA-Pf5gBZ>r4YX3Mav1kKY(ulAJ0Q*jB)YhviHK)w!TJsi3^dMa$L@^{` z_De`fF4;M87vM3Ph9SzCoCi$#Fsd38u!^0#*sPful^p5oI(xGU?yeYjn;Hq1!wzFk zG&2w}W3`AX4bxoVm03y>ts{KaDf!}b&7$(P4KAMP=vK5?1In^-YYNtx1f#}+2QK@h zeSeAI@E6Z8a?)>sZ`fbq9_snl6LCu6g>o)rO;ijp3|$vig+4t} zylEo7$SEW<_U+qgVcaVhk+4k+C9THI5V10qV*dOV6pPtAI$)QN{!JRBKh-D zk2^{j@bZ}yqW?<#VVuI_27*cI-V~sJiqQv&m07+10XF+#ZnIJdr8t`9s_EE;T2V;B z4UnQUH9EdX%zwh-5&wflY#ve!IWt0UE-My3?L#^Bh%kcgP1q{&26eXLn zTkjJ*w+(|_>Pq0v8{%nX$QZbf)tbJaLY$03;MO=Ic-uqYUmUCuXD>J>o6BCRF=xa% z3R4SK9#t1!K4I_d>tZgE>&+kZ?Q}1qo4&h%U$GfY058s%*=!kac{0Z+4Hwm!)pFLR zJ+5*OpgWUrm0FPI2ib4NPJ+Sk07j(`diti^i#kh&f}i>P4~|d?RFb#!JN)~D@)beox}bw?4VCf^y*`2{4`-@%SFTry2h z>9VBc9#JxEs1+0i2^LR@B1J`B9Ac=#FW=(?2;5;#U$0E0UNag_!jY$&2diQk_n)bT zl5Me_SUvqUjwCqmVcyb`igygB_4YUB*m$h5oeKv3uIF0sk}~es!{D>4r%PC*F~FN3owq5e0|YeUTSG#Vq%&Gk7uwW z0lDo#_wvflqHeRm*}l?}o;EILszBt|EW*zNPmq#?4A+&i0xx^?9obLyY4xx=Y9&^G;xYXYPxG)DOpPg!i_Ccl#3L}6xAAZzNhPK1XaC_~ z!A|mlo?Be*8Nn=a+FhgpOj@G7yYs(Qk(8&|h@_>w8Y^r&5nCqe0V60rRz?b5%J;GYeBqSAjo|K692GxD4` zRZyM2FdI+-jK2}WAZTZ()w_)V{n5tEb@>+JYluDozCb$fA4H)$bzg(Ux{*hXurjO^ zwAxc+UXu=&JV*E59}h3kzQPG4M)X8E*}#_&}w*KEgtX)cU{vm9b$atHa;s>| z+L6&cn8xUL*OSjx4YGjf6{Eq+Q3{!ZyhrL&^6Vz@jGbI%cAM9GkmFlamTbcQGvOlL zmJ?(FI)c86=JEs|*;?h~o)88>12nXlpMR4@yh%qdwFNpct;vMlc=;{FSo*apJ;p}! zAX~t;3tb~VuP|ZW;z$=IHf->F@Ml)&-&Bnb{iQyE#;GZ@C$PzEf6~q}4D>9jic@mTO5x76ulDz@+XAcm35!VSu zT*Gs>;f0b2TNpjU_BjHZ&S6Sqk6V1370+!eppV2H+FY!q*n=GHQ!9Rn6MjY!Jc77A zG7Y!lFp8?TIHN!LXO?gCnsYM-gQxsm=Ek**VmZu7vnuufD7K~GIxfxbsQ@qv2T zPa`tvHB$fFCyZl>3oYg?_wW)C>^_iDOc^B7klnTOoytQH18WkOk)L2BSD0r%xgRSW zQS9elF^?O=_@|58zKLK;(f77l-Zzu}4{fXed2saq!5k#UZAoDBqYQS{sn@j@Vtp|$ zG%gnZ$U|9@u#w1@11Sjl8ze^Co=)7yS(}=;68a3~g;NDe_X^}yJj;~s8xq9ahQ5_r zxAlTMnep*)w1e(TG%tWsjo3RR;yVGPEO4V{Zp?=a_0R#=V^ioQu4YL=BO4r0$$XTX zZfnw#_$V}sDAIDrezGQ+h?q24St0QNug_?{s-pI(^jg`#JRxM1YBV;a@@JQvH8*>> zIJvku74E0NlXkYe_624>znU0J@L<-c=G#F3k4A_)*;ky!C(^uZfj%WB3-*{*B$?9+ zDm$WFp=0(xnt6`vDQV3Jl5f&R(Mp};;q8d3I%Kn>Kx=^;uSVCw0L=gw53%Bp==8Sw zxtx=cs!^-_+i{2OK`Q;913+AXc_&Z5$@z3<)So0CU3;JAv=H?@Zpi~riQ{z-zLtVL z!oF<}@IgJp)Iyz1zVJ42!SPHSkjYNS4%ulVVIXdRuiZ@5Mx8LJS}J#qD^Zi_xQ@>DKDr-_e#>5h3dtje*NcwH_h;i{Sx7}dkdpuW z(yUCjckQsagv*QGMSi9u1`Z|V^}Wjf7B@q%j2DQXyd0nOyqg%m{CK_lAoKlJ7#8M} z%IvR?Vh$6aDWK2W!=i?*<77q&B8O&3?zP(Cs@kapc)&p7En?J;t-TX9abGT#H?TW? ztO5(lPKRuC7fs}zwcUKbRh=7E8wzTsa#Z{a`WR}?UZ%!HohN}d&xJ=JQhpO1PI#>X zHkb>pW04pU%Bj_mf~U}1F1=wxdBZu1790>3Dm44bQ#F=T4V3&HlOLsGH)+AK$cHk6 zia$=$kog?)07HCL*PI6}DRhpM^*%I*kHM<#1Se+AQ!!xyhcy6j7`iDX7Z-2i73_n# zas*?7LkxS-XSqv;YBa zW_n*32D(HTYQ0$feV_Fru1ZxW0g&iwqixPX3=9t4o)o|kOo79V$?$uh?#8Q8e>4e)V6;_(x&ViUVxma+i25qea;d-oK7ouuDsB^ab{ zu1qjQ%`n56VtxBE#0qAzb7lph`Eb-}TYpXB!H-}3Ykqyp`otprp7{VEuW*^IR2n$Fb99*nAtqT&oOFIf z@w*6>YvOGw@Ja?Pp1=whZqydzx@9X4n^2!n83C5{C?G@|E?&$?p*g68)kNvUTJ)I6 z1Q|(#UuP6pj78GUxq11m-GSszc+)X{C2eo-?8ud9sB=3(D47v?`JAa{V(IF zPZQ_0AY*9M97>Jf<o%#O_%Wq}8>YM=q0|tGY+hlXcpE=Z4Od z`NT7Hu2hnvRoqOw@g1f=bv`+nba{GwA$Ak0INlqI1k<9!x_!sL()h?hEWoWrdU3w` zZ%%)VR+Bc@_v!C#koM1p-3v_^L6)_Ktj4HE>aUh%2XZE@JFMOn)J~c`_7VWNb9c-N z2b|SZMR4Z@E7j&q&9(6H3yjEu6HV7{2!1t0lgizD;mZ9$r(r7W5G$ky@w(T_dFnOD z*p#+z$@pKE+>o@%eT(2-p_C}wbQ5s(%Sn_{$HDN@MB+Ev?t@3dPy`%TZ!z}AThZSu zN<1i$siJhXFdjV zP*y|V<`V8t=h#XTRUR~5`c`Z9^-`*BZf?WAehGdg)E2Je)hqFa!k{V(u+(hTf^Yq& zoruUh2(^3pe)2{bvt4&4Y9CY3js)PUHtd4rVG57}uFJL)D(JfSIo^{P=7liFXG zq5yqgof0V8paQcP!gy+;^pp-DA5pj=gbMN0eW=-eY+N8~y+G>t+x}oa!5r>tW$xhI zPQSv=pi;~653Gvf6~*JcQ%t1xOrH2l3Zy@8AoJ+wz@daW@m7?%LXkr!bw9GY@ns3e zSfuWF_gkWnesv?s3I`@}NgE2xwgs&rj?kH-FEy82=O8`+szN ziHch`vvS`zNfap14!&#i9H@wF7}yIPm=UB%(o(}F{wsZ(wA0nJ2aD^@B41>>o-_U6 zUqD~vdo48S8~FTb^+%#zcbQiiYoDKYcj&$#^;Smmb+Ljp(L=1Kt_J!;0s%1|JK}Wi z;={~oL!foo5n8=}rs6MmUW~R&;SIJO3TL4Ky?kh+b2rT9B1Jl4>#Uh-Bec z`Hsp<==#UEW6pGPhNk8H!!DUQR~#F9jEMI6T*OWfN^Ze&X(4nV$wa8QUJ>oTkruH# zm~O<`J7Wxseo@FqaZMl#Y(mrFW9AHM9Kb|XBMqaZ2a)DvJgYipkDD_VUF_PKd~dT7 z#02}bBfPn9a!X!O#83=lbJSK#E}K&yx-HI#T6ua)6o0{|={*HFusCkHzs|Fn&|C3H zBck1cmfcWVUN&i>X$YU^Sn6k2H;r3zuXbJFz)r5~3$d$tUj(l1?o={MM){kjgqXRO zc5R*#{;V7AQh|G|)jLM@wGAK&rm2~@{Pewv#06pHbKn#wL0P6F1!^qw9g&cW3Z=9} zj)POhOlwsh@eF=>z?#sIs*C-Nl(yU!#DaiaxhEs#iJqQ8w%(?+6lU02MYSeDkr!B- zPjMv+on6OLXgGnAtl(ao>|X2Y8*Hb}GRW5}-IzXnoo-d0!m4Vy$GS!XOLy>3_+UGs z2D|YcQx@M#M|}TDOetGi{9lGo9m-=0-^+nKE^*?$^uHkxZh}I{#UTQd;X!L+W@jm( zDg@N4+lUqI92o_rNk{3P>1gxAL=&O;x)ZT=q1mk0kLlE$WeWuY_$0`0jY-Kkt zP*|m3AF}Ubd=`<>(Xg0har*_@x2YH}bn0Wk*OZz3*e5;Zc;2uBdnl8?&XjupbkOeNZsNh6pvsq_ydmJI+*z**{I{0K)-;p1~k8cpJXL$^t!-`E}=*4G^-E8>H!LjTPxSx zcF+cS`ommfKMhNSbas^@YbTpH1*RFrBuATUR zt{oFWSk^$xU&kbFQ;MCX22RAN5F6eq9UfR$ut`Jw--p2YX)A*J69m^!oYfj2y7NYcH6&r+0~_sH^c^nzeN1AU4Ga7=FlR{S|Mm~MpzY0$Z+p2W(a={b-pR9EO1Rs zB%KY|@wLcAA@)KXi!d2_BxrkhDn`DT1=Dec}V!okd{$+wK z4E{n8R*xKyci1(CnNdhf$Dp2(Jpof0-0%-38X=Dd9PQgT+w%Lshx9+loPS~MOm%ZT zt%2B2iL_KU_ita%N>xjB!#71_3=3c}o zgeW~^U_ZTJQ2!PqXulQd=3b=XOQhwATK$y(9$#1jOQ4}4?~l#&nek)H(04f(Sr=s| zWv7Lu1=%WGk4FSw^;;!8&YPM)pQDCY9DhU`hMty1@sq1=Tj7bFsOOBZOFlpR`W>-J$-(kezWJj;`?x-v>ev{*8V z8p|KXJPV$HyQr1A(9LVrM47u-XpcrIyO`yWvx1pVYc&?154aneRpLqgx)EMvRaa#|9?Wwqs2+W8n5~79G z(}iCiLk;?enn}ew`HzhG+tu+Ru@T+K5juvZN)wY;x6HjvqD!&!)$$;1VAh~7fg0K| zEha#aN=Yv|3^~YFH}cc38ovVb%L|g@9W6fo(JtT6$fa?zf@Ct88e}m?i)b*Jgc{fl zExfdvw-BYDmH6>(4QMt#p0;FUIQqkhD}aH?a7)_%JtA~soqj{ppP_82yi9kaxuK>~ ze_)Zt>1?q=ZH*kF{1iq9sr*tVuy=u>Zev}!gEZx@O6-fjyu9X00gpIl-fS_pzjpqJ z1yqBmf9NF!jaF<+YxgH6oXBdK)sH(>VZ)1siyA$P<#KDt;8NT*l_0{xit~5j1P)FN zI8hhYKhQ)i z37^aP13B~u65?sg+_@2Kr^iWHN=U;EDSZ@2W2!5ALhGNWXnFBY%7W?1 z=HI9JzQ-pLKZDYTv<0-lt|6c-RwhxZ)mU2Os{bsX_i^@*fKUj8*aDO5pks=qn3Dv6 zwggpKLuyRCTVPwmw1r}B#AS}?X7b837UlXwp~E2|PJw2SGVueL7){Y&z!jL!XN=0i zU^Eig`S2`{+gU$68aRdWx?BZ{sU_f=8sn~>s~M?GU~`fH5kCc; z8ICp+INM3(3{#k32RZdv6b9MQYdZXNuk7ed8;G?S2nT+NZBG=Tar^KFl2SvhW$bGW#kdWL-I)s_IqVnCDDM9fm8g;P;8 z7t4yZn3^*NQfx7SwmkzP$=fwdC}bafQSEF@pd&P8@H#`swGy_rz;Z?Ty5mkS%>m#% zp_!m9e<()sfKiY(nF<1zBz&&`ZlJf6QLvLhl`_``%RW&{+O>Xhp;lwSsyRqGf=RWd zpftiR`={2(siiPAS|p}@q=NhVc0ELprt%=fMXO3B)4ryC2LT(o=sLM7hJC!}T1@)E zA3^J$3&1*M6Xq>03FX`R&w*NkrZE?FwU+Muut;>qNhj@bX17ZJxnOlPSZ=Zeiz~T_ zOu#yc3t6ONHB;?|r4w+pI)~KGN;HOGC)txxiUN8#mexj+W(cz%9a4sx|IRG=}ia zuEBuba3AHsV2feqw-3MvuL`I+2|`Ud4~7ZkN=JZ;L20|Oxna5vx1qbIh#k2O4$RQF zo`tL()zxaqibg^GbB+BS5#U{@K;WWQj~GcB1zb}zJkPwH|5hZ9iH2308!>_;%msji zJHSL~s)YHBR=Koa1mLEOHos*`gp=s8KA-C zu0aE+W!#iJ*0xqKm3A`fUGy#O+X+5W36myS>Uh2!R*s$aCU^`K&KKLCCDkejX2p=5 z%o7-fl03x`gaSNyr?3_JLv?2RLS3F*8ub>Jd@^Cc17)v8vYEK4aqo?OS@W9mt%ITJ z9=S2%R8M){CugT@k~~0x`}Vl!svYqX=E)c_oU6o}#Hb^%G1l3BudxA{F*tbjG;W_>=xV73pKY53v%>I)@D36I_@&p$h|Aw zonQS`07z_F#@T-%@-Tb|)7;;anoD_WH>9ewFy(ZcEOM$#Y)8>qi7rCnsH9GO-_7zF zu*C87{Df1P4TEOsnzZ@H%&lvV(3V@;Q!%+OYRp`g05PjY^gL$^$-t0Y>H*CDDs?FZly*oZ&dxvsxaUWF!{em4{A>n@vpXg$dwvt@_rgmHF z-MER`ABa8R-t_H*kv>}CzOpz;!>p^^9ztHMsHL|SRnS<-y5Z*r(_}c4=fXF`l^-i}>e7v!qs_jv zqvWhX^F=2sDNWA9c@P0?lUlr6ecrTKM%pNQ^?*Lq?p-0~?_j50xV%^(+H>sMul#Tw zeciF*1=?a7cI(}352%>LO96pD+?9!fNyl^9v3^v&Y4L)mNGK0FN43&Xf8jUlxW1Bw zyiu2;qW-aGNhs=zbuoxnxiwZ3{PFZM#Kw)9H@(hgX23h(`Wm~m4&TvoZoYp{plb^> z_#?vXcxd>r7K+1HKJvhed>gtK`TAbJUazUWQY6T~t2af%#<+Veyr%7-#*A#@&*;@g58{i|E%6yC_InGXCOd{L0;$)z#?n7M`re zh!kO{6=>7I?*}czyF7_frt#)s1CFJ_XE&VrDA?Dp3XbvF{qsEJgb&OLSNz_5g?HpK z9)8rsr4JN!Af3G9!#Qn(6zaUDqLN(g2g8*M)Djap?WMK9NKlkC)E2|-g|#-rp%!Gz zAHd%`iq|81efi93m3yTBw3g0j#;Yb2X{mhRAI?&KDmbGqou(2xiRNb^sV}%%Wu0?< z?($L>(#BO*)^)rSgyNRni$i`R4v;GhlCZ8$@e^ROX(p=2_v6Y!%^As zu022)fHdv_-~Yu_H6WVPLpHQx!W%^6j)cBhS`O3QBW#x(eX54d&I22op(N59b*&$v zFiSRY6rOc^(dgSV1>a7-5C;(5S5MvKcM2Jm-LD9TGqDpP097%52V+0>Xqq!! zq4e3vj53SE6i8J`XcQB|MZPP8j;PAOnpGnllH6#Ku~vS42xP*Nz@~y%db7Xi8s09P z1)e%8ys6&M8D=Dt6&t`iKG_4X=!kgRQoh%Z`dc&mlOUqXk-k`jKv9@(a^2-Upw>?< zt5*^DV~6Zedbec4NVl($2T{&b)zA@b#dUyd>`2JC0=xa_fIm8{5um zr-!ApXZhC8@=vC2WyxO|!@0Km)h8ep*`^he92$@YwP>VcdoS5OC^s38e#7RPsg4j+ zbVGG}WRSET&ZfrcR(x~k8n1rTP%CnfUNKUonD$P?FtNFF#cn!wEIab-;jU=B1dHK@ z(;(yAQJ`O$sMn>h;pf^8{JISW%d+@v6@CnXh9n5TXGC}?FI9i-D0OMaIg&mAg=0Kn zNJ7oz5*ReJukD55fUsMuaP+H4tDN&V9zfqF@ zr=#ecUk9wu{0;!+gl;3Bw=Vn^)z$ahVhhw)io!na&9}LmWurLb0zubxK=UEnU*{5P z+SP}&*(iBKSO4{alBHaY^)5Q=mZ+2OwIooJ7*Q5XJ+2|q`9#f?6myq!&oz?klihLq z4C)$XP!BNS0G_Z1&TM>?Jk{S~{F3n83ioli=IO6f%wkvCl(RFFw~j0tb{GvXTx>*sB0McY0s&SNvj4+^h`9nJ_wM>F!Uc>X}9PifQekn0sKI2SAJP!a4h z5cyGTuCj3ZBM^&{dRelIlT^9zcfaAuL5Y~bl!ppSf`wZbK$z#6U~rdclk``e+!qhe z6Qspo*%<)eu6?C;Bp<^VuW6JI|Ncvyn+LlSl;Mp22Bl7ARQ0Xc24%29(ZrdsIPw&-=yHQ7_Vle|5h>AST0 zUGX2Zk34vp?U~IHT|;$U86T+UUHl_NE4m|}>E~6q``7hccCaT^#y+?wD##Q%HwPd8 zV3x4L4|qqu`B$4(LXqDJngNy-{&@aFBvVsywt@X^}iH7P%>bR?ciC$I^U-4Foa`YKI^qDyGK7k%E%c_P=yzAi`YnxGA%DeNd++j3*h^ z=rn>oBd0|~lZ<6YvmkKY*ZJlJ;Im0tqgWu&E92eqt;+NYdxx`eS(4Hw_Jb5|yVvBg z*tbdY^!AN;luEyN4VRhS@-_DC{({ziH{&Z}iGElSV~qvT>L-8G%+yEL zX#MFOhj{InyKG=mvW-<1B@c-}x$vA(nU?>S>0*eN#!SLzQ)Ex7fvQ)S4D<8|I#N$3 zT5Ei`Z?cxBODHX8(Xp73v`IsAYC@9b;t}z0wxVuQSY1J^GRwDPN@qbM-ZF48T$GZ< z8WU+;Pqo?{ghI-KZ-i*ydXu`Ep0Xw^McH_KE9J0S7G;x8Fe`DVG?j3Pv=0YzJ}yZR z%2=oqHiUjvuk0~Ca>Kol4CFi0_xQT~;_F?=u+!kIDl-9g`#ZNZ9HCy17Ga1v^Jv9# z{T4Kb1-AzUxq*MutfOWWZgD*HnFfyYg0&e9f(5tZ>krPF6{VikNeHoc{linPPt#Si z&*g>(c54V8rT_AX!J&bNm-!umPvOR}vDai#`CX___J#=zeB*{4<&2WpaDncZsOkp* zsg<%@@rbrMkR_ux9?LsQxzoBa1s%$BBn6vk#{&&zUwcfzeCBJUwFYSF$08qDsB;gWQN*g!p8pxjofWbqNSZOEKOaTx@+* zwdt5*Q47@EOZ~EZL9s?1o?A%9TJT=Ob_13yyugvPg*e&ZU(r6^k4=2+D-@n=Hv5vu zSXG|hM(>h9^zn=eQ=$6`JO&70&2|%V5Lsx>)(%#;pcOfu>*nk_3HB_BNaH$`jM<^S zcSftDU1?nL;jy)+sfonQN}(}gUW?d_ikr*3=^{G)=tjBtEPe>TO|0ddVB zTklrSHiW+!#26frPXQQ(YN8DG$PZo?(po(QUCCf_OJC`pw*uey00%gmH!`WJkrKXj2!#6?`T25mTu9OJp2L8z3! z=arrL$ZqxuE{%yV)14Kd>k}j7pxZ6#$Dz8$@WV5p8kTqN<-7W)Q7Gt2{KoOPK_tZ| zf2WG~O5@{qPI+W<4f_;reuFVdO^5`ADC1!JQE|N`s3cq@(0WB!n0uh@*c{=LAd;~} zyGK@hbF-Oo+!nN)@i*O(`@FA#u?o=~e{`4O#5}z&=UkU*50fOrzi11D^&FOqe>wii z?*k+2|EcUs;Gx{!@KBT~>PAwLrIDT7Th=Utu?~?np@t^gFs?zgX=D${RwOY^WGh-+ z+#4$066ISh8eYW#FXWp~S`<*%O^ZuItL1Tyqt8#tZ zY120E;^VG`!lZn&3sPd$RkdHpU#|w+bYV)pJC|SH9g%|5IkxVTQcBA4CL0}$&}ef@ zW^Vtj%M;;_1xxP9x#ex17&4N*{ksO*_4O}xYu(p*JkL#yr}@7b)t5X?%CY<+s5_MJ zuiqt+N_;A(_)%lumoyRFixWa-M7qK_9s6<1X?JDa9fP!+_6u~~M$5L=ipB=7(j#f< zZ34J%=bs549%~_mA(|={uZNs_0?o7;-LBP(ZRnkd{-^|2|=4vUTmtByHL8 zEph`(LSEzQj68a+`d$V<45J7cyv^#|^|%fD#si1Nx!4NW*`l*{->HEWNh6-|g>-=r zXmQ|-i}Ku$ndUeHQ^&ieT!Lf}vf6GaqW9$DJ2NWrqwPY%%4nip$@vK$nRp*_C-v<| zuKz~ZyN&<%!NS26&x?jhy+@awJipMQ-8(X4#Ae5??U<1QMt1l9R=w9fAnEF}NYu$2 z>6}Vkc zIb*A?G*z8^IvibmBKn_u^5&T_1oey0gZS2~obf(#xk=erZGTEdQnt3DMGM+0oPwss zj5zXD;(oWhB_T@~Ig#9@v)AKtXu3>Inmgf@A|-lD-1U>cNyl3h?ADD9)GG4}zUGPk zZzaXe!~Kf?<~@$G?Uql3t8jy9{2!doq4=J}j9ktTxss{p6!9UdjyDERlA*xZ!=Q)KDs5O)phz>Vq3BNGoM(H|=1*Q4$^2fTZw z(%nq1P|5Rt81}SYJpEEzMPl5VJsV5&4e)ZWKDyoZ>1EwpkHx-AQVQc8%JMz;{H~p{=FXV>jIxvm4X*qv52e?Y-f%DJ zxEA165GikEASQ^fH6K#d!Tpu2HP{sFs%E=e$gYd$aj$+xue6N+Wc(rAz~wUsk2`(b z8Kvmyz%bKQxpP}~baG-rwYcYCvkHOi zlkR<=>ZBTU*8RF_d#Bl@zZsRIhx<%~Z@Z=ik z>adw3!DK(8R|q$vy{FTxw%#xliD~6qXmY^7_9kthVPTF~Xy1CfBqbU~?1QmxmU=+k z(ggxvEuA;0e&+ci-zQR{-f7aO{O(Pz_OsEjLh_K>MbvoZ4nxtk5u{g@nPv)cgW_R} z9}EA4K4@z0?7ue}Z(o~R(X&FjejUI2g~08PH1E4w>9o{)S(?1>Z0XMvTb|;&EuyOE zGvWNpYX)Nv<8|a^;1>bh#&znEcl-r!T#pn= z4$?Yudha6F%4b>*8@=BdtXXY4N+`U4Dmx$}>HeVJk-QdTG@t!tVT#0(LeV0gvqyyw z2sEp^9eY0N`u10Tm4n8No&A=)IeEC|gnmEXoNSzu!1<4R<%-9kY_8~5Ej?zRegMn78wuMs#;i&eUA0Zk_RXQ3b&TT} z;SCI=7-FUB@*&;8|n>(_g^HGf3@QODE3LpmX~ELnymQm{Sx9xrKS zK29p~?v@R$0=v6Dr5aW>-!{+h@?Q58|Kz8{{W`%J+lDAdb&M5VHrX_mDY;1-JLnf)ezmPau$)1;=`-FU=-r-83tX=C`S#}GZufju zQ>sXNT0Ny=k@nc%cFnvA_i4SC)?_ORXHq8B4D%el1uPX`c~uG#S1M7C+*MMqLw78E zhY2dI8@+N^qrMI1+;TUda(vGqGSRyU{Fnm`aqrr7bz42c5xsOO-~oZpkzorD1g}Y<6rk&3>PsSGy}W?MtqFky@A(X# zIuNZK0cK?^=;PUAu>j0#HtjbHCV*6?jzA&OoE$*Jlga*}LF`SF?WLhv1O|zqC<>*> zYB;#lsYKx0&kH@BFpW8n*yDcc6?;_zaJs<-jPSkCsSX-!aV=P5kUgF@Nu<{a%#K*F z134Q{9|YX7X(v$62_cY3^G%t~rD>Q0z@)1|zs)vjJ6Jq9;7#Ki`w+eS**En?7;n&7 zu==V3T&eFboN3ZiMx3D8qYc;VjFUk_H-WWCau(VFXSQf~viH0L$gwD$UfFHqNcgN`x}M+YQ6RnN<+@t>JUp#)9YOkqst-Ga?{FsDpEeX0(5v{0J~SEbWiL zXC2}M4?UH@u&|;%0y`eb33ldo4~z-x8zY!oVmV=c+f$m?RfDC35mdQ2E>Pze7KWP- z>!Bh<&57I+O_^s}9Tg^k)h7{xx@0a0IA~GAOt2yy!X%Q$1rt~LbTB6@Du!_0%HV>N zlf)QI1&gvERKwso23mJ!Ou6ZS#zCS5W`gxE5T>C#E|{i<1D35C222I33?Njaz`On7 zi<+VWFP6D{e-{yiN#M|Jgk<44u1TiMI78S5W`Sdb5f+{zu34s{CfWN7a3Cf^@L%!& zN$?|!!9j2c)j$~+R6n#891w-z8(!oBpL2K=+%a$r2|~8-(vQj5_XT`<0Ksf;oP+tz z9CObS!0m)Tgg`K#xBM8B(|Z)Wb&DYL{WTYv`;A=q6~Nnx2+!lTIXtj8J7dZE!P_{z z#f8w6F}^!?^KE#+ZDv+xd5O&3EmomZzsv?>E-~ygGum45fk!SBN&|eo1rKw^?aZJ4 E2O(~oYXATM diff --git a/.framework/java/backend/gradle/wrapper/gradle-wrapper.properties b/.framework/java/backend/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index a595206..0000000 --- a/.framework/java/backend/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/.framework/java/backend/gradlew b/.framework/java/backend/gradlew deleted file mode 100755 index 4f906e0..0000000 --- a/.framework/java/backend/gradlew +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" diff --git a/.framework/java/backend/gradlew.bat b/.framework/java/backend/gradlew.bat deleted file mode 100644 index 107acd3..0000000 --- a/.framework/java/backend/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/.framework/java/backend/src/main/java/io/spring/AnythinkMarketApplication.java b/.framework/java/backend/src/main/java/io/spring/AnythinkMarketApplication.java deleted file mode 100644 index 8469390..0000000 --- a/.framework/java/backend/src/main/java/io/spring/AnythinkMarketApplication.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.spring; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class AnythinkMarketApplication { - - public static void main(String[] args) { - SpringApplication.run(AnythinkMarketApplication.class, args); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/JacksonCustomizations.java b/.framework/java/backend/src/main/java/io/spring/JacksonCustomizations.java deleted file mode 100644 index 874a46e..0000000 --- a/.framework/java/backend/src/main/java/io/spring/JacksonCustomizations.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.spring; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import java.io.IOException; -import org.joda.time.DateTime; -import org.joda.time.format.ISODateTimeFormat; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class JacksonCustomizations { - - @Bean - public Module anythinkMarkerModules() { - return new AnythinkMarketModules(); - } - - public static class AnythinkMarketModules extends SimpleModule { - public AnythinkMarketModules() { - addSerializer(DateTime.class, new DateTimeSerializer()); - } - } - - public static class DateTimeSerializer extends StdSerializer { - - protected DateTimeSerializer() { - super(DateTime.class); - } - - @Override - public void serialize(DateTime value, JsonGenerator gen, SerializerProvider provider) - throws IOException { - if (value == null) { - gen.writeNull(); - } else { - gen.writeString(ISODateTimeFormat.dateTime().withZoneUTC().print(value)); - } - } - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/MyBatisConfig.java b/.framework/java/backend/src/main/java/io/spring/MyBatisConfig.java deleted file mode 100644 index d1f741c..0000000 --- a/.framework/java/backend/src/main/java/io/spring/MyBatisConfig.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.spring; - -import org.springframework.context.annotation.Configuration; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -@Configuration -@EnableTransactionManagement -public class MyBatisConfig {} diff --git a/.framework/java/backend/src/main/java/io/spring/Util.java b/.framework/java/backend/src/main/java/io/spring/Util.java deleted file mode 100644 index d2512ac..0000000 --- a/.framework/java/backend/src/main/java/io/spring/Util.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.spring; - -public class Util { - public static boolean isEmpty(String value) { - return value == null || value.isEmpty(); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/CommentsApi.java b/.framework/java/backend/src/main/java/io/spring/api/CommentsApi.java deleted file mode 100644 index 63e4a0a..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/CommentsApi.java +++ /dev/null @@ -1,94 +0,0 @@ -package io.spring.api; - -import com.fasterxml.jackson.annotation.JsonRootName; -import io.spring.api.exception.ResourceNotFoundException; -import io.spring.application.CommentQueryService; -import io.spring.application.data.CommentData; -import io.spring.core.comment.Comment; -import io.spring.core.comment.CommentRepository; -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.user.User; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(path = "/api/items/{slug}/comments") -@AllArgsConstructor -public class CommentsApi { - private ItemRepository itemRepository; - private CommentRepository commentRepository; - private CommentQueryService commentQueryService; - - @PostMapping - public ResponseEntity createComment( - @PathVariable("slug") String slug, - @AuthenticationPrincipal User user, - @Valid @RequestBody NewCommentParam newCommentParam) { - Item item = itemRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); - Comment comment = new Comment(newCommentParam.getBody(), user.getId(), item.getId()); - commentRepository.save(comment); - return ResponseEntity.status(201) - .body(commentResponse(commentQueryService.findById(comment.getId(), user).get())); - } - - @GetMapping - public ResponseEntity getComments( - @PathVariable("slug") String slug, @AuthenticationPrincipal User user) { - Item item = itemRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); - List comments = commentQueryService.findByItemId(item.getId(), user); - return ResponseEntity.ok( - new HashMap() { - { - put("comments", comments); - } - }); - } - - @RequestMapping(path = "{id}", method = RequestMethod.DELETE) - public ResponseEntity deleteComment( - @PathVariable("slug") String slug, - @PathVariable("id") String commentId, - @AuthenticationPrincipal User user) { - Item item = itemRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); - return commentRepository - .findById(item.getId(), commentId) - .map( - comment -> { - commentRepository.remove(comment); - return ResponseEntity.noContent().build(); - }) - .orElseThrow(ResourceNotFoundException::new); - } - - private Map commentResponse(CommentData commentData) { - return new HashMap() { - { - put("comment", commentData); - } - }; - } -} - -@Getter -@NoArgsConstructor -@JsonRootName("comment") -class NewCommentParam { - @NotBlank(message = "can't be empty") - private String body; -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/CurrentUserApi.java b/.framework/java/backend/src/main/java/io/spring/api/CurrentUserApi.java deleted file mode 100644 index 07f9dc9..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/CurrentUserApi.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.spring.api; - -import io.spring.application.UserQueryService; -import io.spring.application.data.UserData; -import io.spring.application.data.UserWithToken; -import io.spring.application.user.UpdateUserCommand; -import io.spring.application.user.UpdateUserParam; -import io.spring.application.user.UserService; -import io.spring.core.user.User; -import jakarta.validation.Valid; -import java.util.HashMap; -import java.util.Map; -import lombok.AllArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(path = "/api/user") -@AllArgsConstructor -public class CurrentUserApi { - - private UserQueryService userQueryService; - private UserService userService; - - @GetMapping - public ResponseEntity currentUser( - @AuthenticationPrincipal User currentUser, - @RequestHeader(value = "Authorization") String authorization) { - UserData userData = userQueryService.findById(currentUser.getId()).get(); - return ResponseEntity.ok( - userResponse(new UserWithToken(userData, authorization.split(" ")[1]))); - } - - @PutMapping - public ResponseEntity updateProfile( - @AuthenticationPrincipal User currentUser, - @RequestHeader("Authorization") String token, - @Valid @RequestBody UpdateUserParam updateUserParam) { - - userService.updateUser(new UpdateUserCommand(currentUser, updateUserParam)); - UserData userData = userQueryService.findById(currentUser.getId()).get(); - return ResponseEntity.ok(userResponse(new UserWithToken(userData, token.split(" ")[1]))); - } - - private Map userResponse(UserWithToken userWithToken) { - return new HashMap() { - { - put("user", userWithToken); - } - }; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/ItemApi.java b/.framework/java/backend/src/main/java/io/spring/api/ItemApi.java deleted file mode 100644 index 6384db4..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/ItemApi.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.spring.api; - -import io.spring.api.exception.NoAuthorizationException; -import io.spring.api.exception.ResourceNotFoundException; -import io.spring.application.ItemQueryService; -import io.spring.application.data.ItemData; -import io.spring.application.item.ItemCommandService; -import io.spring.application.item.UpdateItemParam; -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.service.AuthorizationService; -import io.spring.core.user.User; -import jakarta.validation.Valid; -import java.util.HashMap; -import java.util.Map; -import lombok.AllArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(path = "/api/items/{slug}") -@AllArgsConstructor -public class ItemApi { - private ItemQueryService itemQueryService; - private ItemRepository itemRepository; - private ItemCommandService itemCommandService; - - @GetMapping - public ResponseEntity item( - @PathVariable("slug") String slug, @AuthenticationPrincipal User user) { - return itemQueryService - .findBySlug(slug, user) - .map(itemData -> ResponseEntity.ok(itemResponse(itemData))) - .orElseThrow(ResourceNotFoundException::new); - } - - @PutMapping - public ResponseEntity updateItem( - @PathVariable("slug") String slug, - @AuthenticationPrincipal User user, - @Valid @RequestBody UpdateItemParam updateItemParam) { - return itemRepository - .findBySlug(slug) - .map( - item -> { - if (!AuthorizationService.canWriteItem(user, item)) { - throw new NoAuthorizationException(); - } - Item updatedItem = itemCommandService.updateItem(item, updateItemParam); - return ResponseEntity.ok( - itemResponse(itemQueryService.findBySlug(updatedItem.getSlug(), user).get())); - }) - .orElseThrow(ResourceNotFoundException::new); - } - - @DeleteMapping - public ResponseEntity deleteItem( - @PathVariable("slug") String slug, @AuthenticationPrincipal User user) { - return itemRepository - .findBySlug(slug) - .map( - item -> { - if (!AuthorizationService.canWriteItem(user, item)) { - throw new NoAuthorizationException(); - } - itemRepository.remove(item); - return ResponseEntity.noContent().build(); - }) - .orElseThrow(ResourceNotFoundException::new); - } - - private Map itemResponse(ItemData itemData) { - return new HashMap() { - { - put("item", itemData); - } - }; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/ItemFavoriteApi.java b/.framework/java/backend/src/main/java/io/spring/api/ItemFavoriteApi.java deleted file mode 100644 index 48f7a47..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/ItemFavoriteApi.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.spring.api; - -import io.spring.api.exception.ResourceNotFoundException; -import io.spring.application.ItemQueryService; -import io.spring.application.data.ItemData; -import io.spring.core.favorite.ItemFavorite; -import io.spring.core.favorite.ItemFavoriteRepository; -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.user.User; -import java.util.HashMap; -import lombok.AllArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(path = "/api/items/{slug}/favorite") -@AllArgsConstructor -public class ItemFavoriteApi { - private ItemFavoriteRepository itemFavoriteRepository; - private ItemRepository itemRepository; - private ItemQueryService itemQueryService; - - @PostMapping - public ResponseEntity favoriteItem( - @PathVariable("slug") String slug, @AuthenticationPrincipal User user) { - Item item = itemRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); - ItemFavorite itemFavorite = new ItemFavorite(item.getId(), user.getId()); - itemFavoriteRepository.save(itemFavorite); - return responseItemData(itemQueryService.findBySlug(slug, user).get()); - } - - @DeleteMapping - public ResponseEntity unfavoriteItem( - @PathVariable("slug") String slug, @AuthenticationPrincipal User user) { - Item item = itemRepository.findBySlug(slug).orElseThrow(ResourceNotFoundException::new); - itemFavoriteRepository - .find(item.getId(), user.getId()) - .ifPresent( - favorite -> { - itemFavoriteRepository.remove(favorite); - }); - return responseItemData(itemQueryService.findBySlug(slug, user).get()); - } - - private ResponseEntity> responseItemData(final ItemData itemData) { - return ResponseEntity.ok( - new HashMap() { - { - put("item", itemData); - } - }); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/ItemsApi.java b/.framework/java/backend/src/main/java/io/spring/api/ItemsApi.java deleted file mode 100644 index e5fb145..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/ItemsApi.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.spring.api; - -import io.spring.application.ItemQueryService; -import io.spring.application.Page; -import io.spring.application.item.ItemCommandService; -import io.spring.application.item.NewItemParam; -import io.spring.core.item.Item; -import io.spring.core.user.User; -import io.spring.infrastructure.service.SendEventService; -import jakarta.validation.Valid; -import java.util.HashMap; -import java.util.Map; -import lombok.AllArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(path = "/api/items") -@AllArgsConstructor -public class ItemsApi { - private ItemCommandService itemCommandService; - private ItemQueryService itemQueryService; - - @PostMapping - public ResponseEntity createItem( - @Valid @RequestBody NewItemParam newItemParam, @AuthenticationPrincipal User user) { - Item item = itemCommandService.createItem(newItemParam, user); - - SendEventService sendEventService = new SendEventService(); - Map metadata = new HashMap<>(); - metadata.put("item", item.getTitle()); - sendEventService.sendEvent("item_created", metadata); - - return ResponseEntity.ok( - new HashMap() { - { - put("item", itemQueryService.findById(item.getId(), user).get()); - } - }); - } - - @GetMapping(path = "feed") - public ResponseEntity getFeed( - @RequestParam(value = "offset", defaultValue = "0") int offset, - @RequestParam(value = "limit", defaultValue = "20") int limit, - @AuthenticationPrincipal User user) { - return ResponseEntity.ok(itemQueryService.findUserFeed(user, new Page(offset, limit))); - } - - @GetMapping - public ResponseEntity getItems( - @RequestParam(value = "offset", defaultValue = "0") int offset, - @RequestParam(value = "limit", defaultValue = "20") int limit, - @RequestParam(value = "tag", required = false) String tag, - @RequestParam(value = "favorited", required = false) String favoritedBy, - @RequestParam(value = "seller", required = false) String seller, - @AuthenticationPrincipal User user) { - return ResponseEntity.ok( - itemQueryService.findRecentItems(tag, seller, favoritedBy, new Page(offset, limit), user)); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/PingApi.java b/.framework/java/backend/src/main/java/io/spring/api/PingApi.java deleted file mode 100644 index 6883ac6..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/PingApi.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.spring.api; - -import io.spring.infrastructure.service.SendEventService; -import java.util.HashMap; -import lombok.AllArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(path = "api/ping") -@AllArgsConstructor -public class PingApi { - @GetMapping - public ResponseEntity ping() { - SendEventService sendEventService = new SendEventService(); - String response = sendEventService.sendEvent("ping", new HashMap<>()); - return ResponseEntity.ok(response); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/ProfileApi.java b/.framework/java/backend/src/main/java/io/spring/api/ProfileApi.java deleted file mode 100644 index 61429e1..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/ProfileApi.java +++ /dev/null @@ -1,78 +0,0 @@ -package io.spring.api; - -import io.spring.api.exception.ResourceNotFoundException; -import io.spring.application.ProfileQueryService; -import io.spring.application.data.ProfileData; -import io.spring.core.user.FollowRelation; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import java.util.HashMap; -import java.util.Optional; -import lombok.AllArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(path = "api/profiles/{username}") -@AllArgsConstructor -public class ProfileApi { - private ProfileQueryService profileQueryService; - private UserRepository userRepository; - - @GetMapping - public ResponseEntity getProfile( - @PathVariable("username") String username, @AuthenticationPrincipal User user) { - return profileQueryService - .findByUsername(username, user) - .map(this::profileResponse) - .orElseThrow(ResourceNotFoundException::new); - } - - @PostMapping(path = "follow") - public ResponseEntity follow( - @PathVariable("username") String username, @AuthenticationPrincipal User user) { - return userRepository - .findByUsername(username) - .map( - target -> { - FollowRelation followRelation = new FollowRelation(user.getId(), target.getId()); - userRepository.saveRelation(followRelation); - return profileResponse(profileQueryService.findByUsername(username, user).get()); - }) - .orElseThrow(ResourceNotFoundException::new); - } - - @DeleteMapping(path = "follow") - public ResponseEntity unfollow( - @PathVariable("username") String username, @AuthenticationPrincipal User user) { - Optional userOptional = userRepository.findByUsername(username); - if (userOptional.isPresent()) { - User target = userOptional.get(); - return userRepository - .findRelation(user.getId(), target.getId()) - .map( - relation -> { - userRepository.removeRelation(relation); - return profileResponse(profileQueryService.findByUsername(username, user).get()); - }) - .orElseThrow(ResourceNotFoundException::new); - } else { - throw new ResourceNotFoundException(); - } - } - - private ResponseEntity profileResponse(ProfileData profile) { - return ResponseEntity.ok( - new HashMap() { - { - put("profile", profile); - } - }); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/TagsApi.java b/.framework/java/backend/src/main/java/io/spring/api/TagsApi.java deleted file mode 100644 index 9720f37..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/TagsApi.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.spring.api; - -import io.spring.application.TagsQueryService; -import java.util.HashMap; -import lombok.AllArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping(path = "api/tags") -@AllArgsConstructor -public class TagsApi { - private TagsQueryService tagsQueryService; - - @GetMapping - public ResponseEntity getTags() { - return ResponseEntity.ok( - new HashMap() { - { - put("tags", tagsQueryService.allTags()); - } - }); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/UsersApi.java b/.framework/java/backend/src/main/java/io/spring/api/UsersApi.java deleted file mode 100644 index 07f2771..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/UsersApi.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.spring.api; - -import static org.springframework.web.bind.annotation.RequestMethod.POST; - -import com.fasterxml.jackson.annotation.JsonRootName; -import io.spring.api.exception.InvalidAuthenticationException; -import io.spring.application.UserQueryService; -import io.spring.application.data.UserData; -import io.spring.application.data.UserWithToken; -import io.spring.application.user.RegisterParam; -import io.spring.application.user.UserService; -import io.spring.core.service.JwtService; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.service.SendEventService; -import jakarta.validation.Valid; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@AllArgsConstructor -public class UsersApi { - private UserRepository userRepository; - private UserQueryService userQueryService; - private PasswordEncoder passwordEncoder; - private JwtService jwtService; - private UserService userService; - - @RequestMapping(path = "/api/users", method = POST) - public ResponseEntity createUser(@Valid @RequestBody RegisterParam registerParam) { - User user = userService.createUser(registerParam); - - SendEventService sendEventService = new SendEventService(); - Map metadata = new HashMap<>(); - metadata.put("username", user.getUsername()); - sendEventService.sendEvent("user_created", metadata); - - UserData userData = userQueryService.findById(user.getId()).get(); - return ResponseEntity.status(201) - .body(userResponse(new UserWithToken(userData, jwtService.toToken(user)))); - } - - @RequestMapping(path = "/api/users/login", method = POST) - public ResponseEntity userLogin(@Valid @RequestBody LoginParam loginParam) { - Optional optional = userRepository.findByEmail(loginParam.getEmail()); - if (optional.isPresent() - && passwordEncoder.matches(loginParam.getPassword(), optional.get().getPassword())) { - UserData userData = userQueryService.findById(optional.get().getId()).get(); - return ResponseEntity.ok( - userResponse(new UserWithToken(userData, jwtService.toToken(optional.get())))); - } else { - throw new InvalidAuthenticationException(); - } - } - - private Map userResponse(UserWithToken userWithToken) { - return new HashMap() { - { - put("user", userWithToken); - } - }; - } -} - -@Getter -@JsonRootName("user") -@NoArgsConstructor -class LoginParam { - @NotBlank(message = "can't be empty") - @Email(message = "should be an email") - private String email; - - @NotBlank(message = "can't be empty") - private String password; -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/exception/CustomizeExceptionHandler.java b/.framework/java/backend/src/main/java/io/spring/api/exception/CustomizeExceptionHandler.java deleted file mode 100644 index 3e7c5c9..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/exception/CustomizeExceptionHandler.java +++ /dev/null @@ -1,109 +0,0 @@ -package io.spring.api.exception; - -import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; - -import jakarta.validation.ConstraintViolation; -import jakarta.validation.ConstraintViolationException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.stream.Collectors; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatusCode; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.context.request.WebRequest; -import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; - -@RestControllerAdvice -public class CustomizeExceptionHandler extends ResponseEntityExceptionHandler { - - @ExceptionHandler({InvalidRequestException.class}) - public ResponseEntity handleInvalidRequest(RuntimeException e, WebRequest request) { - InvalidRequestException ire = (InvalidRequestException) e; - - List errorResources = - ire.getErrors().getFieldErrors().stream() - .map( - fieldError -> - new FieldErrorResource( - fieldError.getObjectName(), - fieldError.getField(), - fieldError.getCode(), - fieldError.getDefaultMessage())) - .collect(Collectors.toList()); - - ErrorResource error = new ErrorResource(errorResources); - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - - return handleExceptionInternal(e, error, headers, UNPROCESSABLE_ENTITY, request); - } - - @ExceptionHandler(InvalidAuthenticationException.class) - public ResponseEntity handleInvalidAuthentication( - InvalidAuthenticationException e, WebRequest request) { - return ResponseEntity.status(UNPROCESSABLE_ENTITY) - .body( - new HashMap() { - { - put("message", e.getMessage()); - } - }); - } - - @Override - protected ResponseEntity handleMethodArgumentNotValid( - MethodArgumentNotValidException e, - HttpHeaders headers, - HttpStatusCode status, - WebRequest request) { - List errorResources = - e.getBindingResult().getFieldErrors().stream() - .map( - fieldError -> - new FieldErrorResource( - fieldError.getObjectName(), - fieldError.getField(), - fieldError.getCode(), - fieldError.getDefaultMessage())) - .collect(Collectors.toList()); - - return ResponseEntity.status(UNPROCESSABLE_ENTITY).body(new ErrorResource(errorResources)); - } - - @ExceptionHandler({ConstraintViolationException.class}) - @ResponseStatus(UNPROCESSABLE_ENTITY) - @ResponseBody - public ErrorResource handleConstraintViolation( - ConstraintViolationException ex, WebRequest request) { - List errors = new ArrayList<>(); - for (ConstraintViolation violation : ex.getConstraintViolations()) { - FieldErrorResource fieldErrorResource = - new FieldErrorResource( - violation.getRootBeanClass().getName(), - getParam(violation.getPropertyPath().toString()), - violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(), - violation.getMessage()); - errors.add(fieldErrorResource); - } - - return new ErrorResource(errors); - } - - private String getParam(String s) { - String[] splits = s.split("\\."); - if (splits.length == 1) { - return s; - } else { - return String.join(".", Arrays.copyOfRange(splits, 2, splits.length)); - } - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/exception/ErrorResource.java b/.framework/java/backend/src/main/java/io/spring/api/exception/ErrorResource.java deleted file mode 100644 index 1c27805..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/exception/ErrorResource.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.spring.api.exception; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonRootName; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import java.util.List; - -@JsonSerialize(using = ErrorResourceSerializer.class) -@JsonIgnoreProperties(ignoreUnknown = true) -@lombok.Getter -@JsonRootName("errors") -public class ErrorResource { - private List fieldErrors; - - public ErrorResource(List fieldErrorResources) { - this.fieldErrors = fieldErrorResources; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/exception/ErrorResourceSerializer.java b/.framework/java/backend/src/main/java/io/spring/api/exception/ErrorResourceSerializer.java deleted file mode 100644 index 2ce3816..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/exception/ErrorResourceSerializer.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.spring.api.exception; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ErrorResourceSerializer extends JsonSerializer { - @Override - public void serialize(ErrorResource value, JsonGenerator gen, SerializerProvider serializers) - throws IOException, JsonProcessingException { - Map> json = new HashMap<>(); - gen.writeStartObject(); - gen.writeObjectFieldStart("errors"); - for (FieldErrorResource fieldErrorResource : value.getFieldErrors()) { - if (!json.containsKey(fieldErrorResource.getField())) { - json.put(fieldErrorResource.getField(), new ArrayList()); - } - json.get(fieldErrorResource.getField()).add(fieldErrorResource.getMessage()); - } - for (Map.Entry> pair : json.entrySet()) { - gen.writeArrayFieldStart(pair.getKey()); - pair.getValue() - .forEach( - content -> { - try { - gen.writeString(content); - } catch (IOException e) { - e.printStackTrace(); - } - }); - gen.writeEndArray(); - } - gen.writeEndObject(); - gen.writeEndObject(); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/exception/FieldErrorResource.java b/.framework/java/backend/src/main/java/io/spring/api/exception/FieldErrorResource.java deleted file mode 100644 index 13d5731..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/exception/FieldErrorResource.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.spring.api.exception; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@JsonIgnoreProperties(ignoreUnknown = true) -@Getter -@AllArgsConstructor -public class FieldErrorResource { - private String resource; - private String field; - private String code; - private String message; -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/exception/InvalidAuthenticationException.java b/.framework/java/backend/src/main/java/io/spring/api/exception/InvalidAuthenticationException.java deleted file mode 100644 index 96af7a8..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/exception/InvalidAuthenticationException.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.spring.api.exception; - -public class InvalidAuthenticationException extends RuntimeException { - - public InvalidAuthenticationException() { - super("invalid email or password"); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/exception/InvalidRequestException.java b/.framework/java/backend/src/main/java/io/spring/api/exception/InvalidRequestException.java deleted file mode 100644 index 68b6c86..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/exception/InvalidRequestException.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.spring.api.exception; - -import org.springframework.validation.Errors; - -@SuppressWarnings("serial") -public class InvalidRequestException extends RuntimeException { - private final Errors errors; - - public InvalidRequestException(Errors errors) { - super(""); - this.errors = errors; - } - - public Errors getErrors() { - return errors; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/exception/NoAuthorizationException.java b/.framework/java/backend/src/main/java/io/spring/api/exception/NoAuthorizationException.java deleted file mode 100644 index 6741423..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/exception/NoAuthorizationException.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.spring.api.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(HttpStatus.FORBIDDEN) -public class NoAuthorizationException extends RuntimeException {} diff --git a/.framework/java/backend/src/main/java/io/spring/api/exception/ResourceNotFoundException.java b/.framework/java/backend/src/main/java/io/spring/api/exception/ResourceNotFoundException.java deleted file mode 100644 index 8401e52..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/exception/ResourceNotFoundException.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.spring.api.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(value = HttpStatus.NOT_FOUND) -public class ResourceNotFoundException extends RuntimeException {} diff --git a/.framework/java/backend/src/main/java/io/spring/api/security/JwtTokenFilter.java b/.framework/java/backend/src/main/java/io/spring/api/security/JwtTokenFilter.java deleted file mode 100644 index b03e9d1..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/security/JwtTokenFilter.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.spring.api.security; - -import io.spring.core.service.JwtService; -import io.spring.core.user.UserRepository; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Collections; -import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.web.filter.OncePerRequestFilter; - -@SuppressWarnings("SpringJavaAutowiringInspection") -public class JwtTokenFilter extends OncePerRequestFilter { - @Autowired private UserRepository userRepository; - @Autowired private JwtService jwtService; - private final String header = "Authorization"; - - @Override - protected void doFilterInternal( - HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - getTokenString(request.getHeader(header)) - .flatMap(token -> jwtService.getSubFromToken(token)) - .ifPresent( - id -> { - if (SecurityContextHolder.getContext().getAuthentication() == null) { - userRepository - .findById(id) - .ifPresent( - user -> { - UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken( - user, null, Collections.emptyList()); - authenticationToken.setDetails( - new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(authenticationToken); - }); - } - }); - - filterChain.doFilter(request, response); - } - - private Optional getTokenString(String header) { - if (header == null) { - return Optional.empty(); - } else { - String[] split = header.split(" "); - if (split.length < 2) { - return Optional.empty(); - } else { - return Optional.ofNullable(split[1]); - } - } - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/api/security/WebSecurityConfig.java b/.framework/java/backend/src/main/java/io/spring/api/security/WebSecurityConfig.java deleted file mode 100644 index 9523e96..0000000 --- a/.framework/java/backend/src/main/java/io/spring/api/security/WebSecurityConfig.java +++ /dev/null @@ -1,80 +0,0 @@ -package io.spring.api.security; - -import java.util.Arrays; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.HttpStatusEntryPoint; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; - -@Configuration -@EnableWebSecurity -public class WebSecurityConfig { - - @Bean - public JwtTokenFilter jwtTokenFilter() { - return new JwtTokenFilter(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.csrf(csrf -> csrf.disable()) - .cors((cors) -> cors.configurationSource(corsConfigurationSource())) - .sessionManagement( - (session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .exceptionHandling( - exceptionHandling -> - exceptionHandling.authenticationEntryPoint( - new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))) - .authorizeHttpRequests( - auth -> - auth.requestMatchers(HttpMethod.OPTIONS, "/**") - .permitAll() - .requestMatchers("/actuator/**") - .permitAll() - .requestMatchers("/error") - .permitAll() - .requestMatchers("/health") - .permitAll() - .requestMatchers("/api/ping") - .permitAll() - .requestMatchers(HttpMethod.GET, "/api/items/feed") - .authenticated() - .requestMatchers(HttpMethod.POST, "/api/users", "/api/users/login") - .permitAll() - .requestMatchers( - HttpMethod.GET, "/api/items/**", "/api/profiles/**", "/api/tags") - .permitAll() - .anyRequest() - .authenticated()); - - http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); - return http.build(); - } - - @Bean - CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(Arrays.asList("*")); - configuration.setAllowedMethods(Arrays.asList("*")); - configuration.setAllowedHeaders(Arrays.asList("*")); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - return source; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/CommentQueryService.java b/.framework/java/backend/src/main/java/io/spring/application/CommentQueryService.java deleted file mode 100644 index dfba6ef..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/CommentQueryService.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.spring.application; - -import io.spring.application.data.CommentData; -import io.spring.core.user.User; -import io.spring.infrastructure.mybatis.readservice.CommentReadService; -import io.spring.infrastructure.mybatis.readservice.UserRelationshipQueryService; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import lombok.AllArgsConstructor; -import org.joda.time.DateTime; -import org.springframework.stereotype.Service; - -@Service -@AllArgsConstructor -public class CommentQueryService { - private CommentReadService commentReadService; - private UserRelationshipQueryService userRelationshipQueryService; - - public Optional findById(String id, User user) { - CommentData commentData = commentReadService.findById(id); - if (commentData == null) { - return Optional.empty(); - } else { - commentData - .getProfileData() - .setFollowing( - userRelationshipQueryService.isUserFollowing( - user.getId(), commentData.getProfileData().getId())); - } - return Optional.ofNullable(commentData); - } - - public List findByItemId(String itemId, User user) { - List comments = commentReadService.findByItemId(itemId); - if (comments.size() > 0 && user != null) { - Set followingSellers = - userRelationshipQueryService.followingSellers( - user.getId(), - comments.stream() - .map(commentData -> commentData.getProfileData().getId()) - .collect(Collectors.toList())); - comments.forEach( - commentData -> { - if (followingSellers.contains(commentData.getProfileData().getId())) { - commentData.getProfileData().setFollowing(true); - } - }); - } - return comments; - } - - public CursorPager findByItemIdWithCursor( - String itemId, User user, CursorPageParameter page) { - List comments = commentReadService.findByItemIdWithCursor(itemId, page); - if (comments.isEmpty()) { - return new CursorPager<>(new ArrayList<>(), page.getDirection(), false); - } - if (user != null) { - Set followingSellers = - userRelationshipQueryService.followingSellers( - user.getId(), - comments.stream() - .map(commentData -> commentData.getProfileData().getId()) - .collect(Collectors.toList())); - comments.forEach( - commentData -> { - if (followingSellers.contains(commentData.getProfileData().getId())) { - commentData.getProfileData().setFollowing(true); - } - }); - } - boolean hasExtra = comments.size() > page.getLimit(); - if (hasExtra) { - comments.remove(page.getLimit()); - } - if (!page.isNext()) { - Collections.reverse(comments); - } - return new CursorPager<>(comments, page.getDirection(), hasExtra); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/CursorPageParameter.java b/.framework/java/backend/src/main/java/io/spring/application/CursorPageParameter.java deleted file mode 100644 index 1953137..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/CursorPageParameter.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.spring.application; - -import io.spring.application.CursorPager.Direction; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -public class CursorPageParameter { - private static final int MAX_LIMIT = 1000; - private int limit = 20; - private T cursor; - private Direction direction; - - public CursorPageParameter(T cursor, int limit, Direction direction) { - setLimit(limit); - setCursor(cursor); - setDirection(direction); - } - - public boolean isNext() { - return direction == Direction.NEXT; - } - - public int getQueryLimit() { - return limit + 1; - } - - private void setCursor(T cursor) { - this.cursor = cursor; - } - - private void setLimit(int limit) { - if (limit > MAX_LIMIT) { - this.limit = MAX_LIMIT; - } else if (limit > 0) { - this.limit = limit; - } - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/CursorPager.java b/.framework/java/backend/src/main/java/io/spring/application/CursorPager.java deleted file mode 100644 index 13d55d4..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/CursorPager.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.spring.application; - -import java.util.List; -import lombok.Getter; - -@Getter -public class CursorPager { - private List data; - private boolean next; - private boolean previous; - - public CursorPager(List data, Direction direction, boolean hasExtra) { - this.data = data; - - if (direction == Direction.NEXT) { - this.previous = false; - this.next = hasExtra; - } else { - this.next = false; - this.previous = hasExtra; - } - } - - public boolean hasNext() { - return next; - } - - public boolean hasPrevious() { - return previous; - } - - public PageCursor getStartCursor() { - return data.isEmpty() ? null : data.get(0).getCursor(); - } - - public PageCursor getEndCursor() { - return data.isEmpty() ? null : data.get(data.size() - 1).getCursor(); - } - - public enum Direction { - PREV, - NEXT - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/DateTimeCursor.java b/.framework/java/backend/src/main/java/io/spring/application/DateTimeCursor.java deleted file mode 100644 index cfcc86b..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/DateTimeCursor.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.spring.application; - -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; - -public class DateTimeCursor extends PageCursor { - - public DateTimeCursor(DateTime data) { - super(data); - } - - @Override - public String toString() { - return String.valueOf(getData().getMillis()); - } - - public static DateTime parse(String cursor) { - if (cursor == null) { - return null; - } - return new DateTime().withMillis(Long.parseLong(cursor)).withZone(DateTimeZone.UTC); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/ItemQueryService.java b/.framework/java/backend/src/main/java/io/spring/application/ItemQueryService.java deleted file mode 100644 index d78a366..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/ItemQueryService.java +++ /dev/null @@ -1,185 +0,0 @@ -package io.spring.application; - -import static java.util.stream.Collectors.toList; - -import io.spring.application.data.ItemData; -import io.spring.application.data.ItemDataList; -import io.spring.application.data.ItemFavoriteCount; -import io.spring.core.user.User; -import io.spring.infrastructure.mybatis.readservice.ItemFavoritesReadService; -import io.spring.infrastructure.mybatis.readservice.ItemReadService; -import io.spring.infrastructure.mybatis.readservice.UserRelationshipQueryService; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import lombok.AllArgsConstructor; -import org.joda.time.DateTime; -import org.springframework.stereotype.Service; - -@Service -@AllArgsConstructor -public class ItemQueryService { - private ItemReadService itemReadService; - private UserRelationshipQueryService userRelationshipQueryService; - private ItemFavoritesReadService itemFavoritesReadService; - - public Optional findById(String id, User user) { - ItemData itemData = itemReadService.findById(id); - if (itemData == null) { - return Optional.empty(); - } else { - if (user != null) { - fillExtraInfo(id, user, itemData); - } - return Optional.of(itemData); - } - } - - public Optional findBySlug(String slug, User user) { - ItemData itemData = itemReadService.findBySlug(slug); - if (itemData == null) { - return Optional.empty(); - } else { - if (user != null) { - fillExtraInfo(itemData.getId(), user, itemData); - } - - setFavoriteCount(Collections.singletonList(itemData)); - return Optional.of(itemData); - } - } - - public CursorPager findRecentItemsWithCursor( - String tag, - String seller, - String favoritedBy, - CursorPageParameter page, - User currentUser) { - List itemIds = itemReadService.findItemsWithCursor(tag, seller, favoritedBy, page); - if (itemIds.size() == 0) { - return new CursorPager<>(new ArrayList<>(), page.getDirection(), false); - } else { - boolean hasExtra = itemIds.size() > page.getLimit(); - if (hasExtra) { - itemIds.remove(page.getLimit()); - } - if (!page.isNext()) { - Collections.reverse(itemIds); - } - - List items = itemReadService.findItems(itemIds); - fillExtraInfo(items, currentUser); - - return new CursorPager<>(items, page.getDirection(), hasExtra); - } - } - - public CursorPager findUserFeedWithCursor( - User user, CursorPageParameter page) { - List followedUsers = userRelationshipQueryService.followedUsers(user.getId()); - if (followedUsers.size() == 0) { - return new CursorPager<>(new ArrayList<>(), page.getDirection(), false); - } else { - List items = itemReadService.findItemsOfSellersWithCursor(followedUsers, page); - boolean hasExtra = items.size() > page.getLimit(); - if (hasExtra) { - items.remove(page.getLimit()); - } - if (!page.isNext()) { - Collections.reverse(items); - } - fillExtraInfo(items, user); - return new CursorPager<>(items, page.getDirection(), hasExtra); - } - } - - public ItemDataList findRecentItems( - String tag, String seller, String favoritedBy, Page page, User currentUser) { - List itemIds = itemReadService.queryItems(tag, seller, favoritedBy, page); - int itemCount = itemReadService.countItem(tag, seller, favoritedBy); - if (itemIds.size() == 0) { - return new ItemDataList(new ArrayList<>(), itemCount); - } else { - List items = itemReadService.findItems(itemIds); - fillExtraInfo(items, currentUser); - return new ItemDataList(items, itemCount); - } - } - - public ItemDataList findUserFeed(User user, Page page) { - List followedUsers = userRelationshipQueryService.followedUsers(user.getId()); - if (followedUsers.size() == 0) { - return new ItemDataList(new ArrayList<>(), 0); - } else { - List items = itemReadService.findItemsOfSellers(followedUsers, page); - int offset = page.getOffset(); - int limit = page.getLimit(); - int endIndex = Math.min(offset + limit, items.size()); - items = items.subList(offset, endIndex); - - fillExtraInfo(items, user); - int count = itemReadService.countFeedSize(followedUsers); - return new ItemDataList(items, count); - } - } - - private void fillExtraInfo(List items, User currentUser) { - setFavoriteCount(items); - if (currentUser != null) { - setIsFavorite(items, currentUser); - setIsFollowingSeller(items, currentUser); - } - } - - private void setIsFollowingSeller(List items, User currentUser) { - Set followingSellers = - userRelationshipQueryService.followingSellers( - currentUser.getId(), - items.stream().map(itemData1 -> itemData1.getProfileData().getId()).collect(toList())); - items.forEach( - itemData -> { - if (followingSellers.contains(itemData.getProfileData().getId())) { - itemData.getProfileData().setFollowing(true); - } - }); - } - - private void setFavoriteCount(List items) { - List favoritesCounts = - itemFavoritesReadService.itemsFavoriteCount( - items.stream().map(ItemData::getId).collect(toList())); - Map countMap = new HashMap<>(); - favoritesCounts.forEach( - item -> { - countMap.put(item.getId(), item.getCount()); - }); - items.forEach(itemData -> itemData.setFavoritesCount(countMap.get(itemData.getId()))); - } - - private void setIsFavorite(List items, User currentUser) { - Set favoritedItems = - itemFavoritesReadService.userFavorites( - items.stream().map(itemData -> itemData.getId()).collect(toList()), currentUser); - - items.forEach( - itemData -> { - if (favoritedItems.contains(itemData.getId())) { - itemData.setFavorited(true); - } - }); - } - - private void fillExtraInfo(String id, User user, ItemData itemData) { - itemData.setFavorited(itemFavoritesReadService.isUserFavorite(user.getId(), id)); - itemData.setFavoritesCount(itemFavoritesReadService.itemFavoriteCount(id)); - itemData - .getProfileData() - .setFollowing( - userRelationshipQueryService.isUserFollowing( - user.getId(), itemData.getProfileData().getId())); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/Node.java b/.framework/java/backend/src/main/java/io/spring/application/Node.java deleted file mode 100644 index e4ccac8..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/Node.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.spring.application; - -public interface Node { - PageCursor getCursor(); -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/Page.java b/.framework/java/backend/src/main/java/io/spring/application/Page.java deleted file mode 100644 index d273e99..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/Page.java +++ /dev/null @@ -1,31 +0,0 @@ -package io.spring.application; - -import lombok.Data; -import lombok.NoArgsConstructor; - -@NoArgsConstructor -@Data -public class Page { - private static final int MAX_LIMIT = 100; - private int offset = 0; - private int limit = 20; - - public Page(int offset, int limit) { - setOffset(offset); - setLimit(limit); - } - - private void setOffset(int offset) { - if (offset > 0) { - this.offset = offset; - } - } - - private void setLimit(int limit) { - if (limit > MAX_LIMIT) { - this.limit = MAX_LIMIT; - } else if (limit > 0) { - this.limit = limit; - } - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/PageCursor.java b/.framework/java/backend/src/main/java/io/spring/application/PageCursor.java deleted file mode 100644 index 0279f3b..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/PageCursor.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.spring.application; - -public abstract class PageCursor { - private T data; - - public PageCursor(T data) { - this.data = data; - } - - public T getData() { - return data; - } - - @Override - public String toString() { - return data.toString(); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/ProfileQueryService.java b/.framework/java/backend/src/main/java/io/spring/application/ProfileQueryService.java deleted file mode 100644 index d92542d..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/ProfileQueryService.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.spring.application; - -import io.spring.application.data.ProfileData; -import io.spring.application.data.UserData; -import io.spring.core.user.User; -import io.spring.infrastructure.mybatis.readservice.UserReadService; -import io.spring.infrastructure.mybatis.readservice.UserRelationshipQueryService; -import java.util.Optional; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@AllArgsConstructor -public class ProfileQueryService { - private UserReadService userReadService; - private UserRelationshipQueryService userRelationshipQueryService; - - public Optional findByUsername(String username, User currentUser) { - UserData userData = userReadService.findByUsername(username); - if (userData == null) { - return Optional.empty(); - } else { - ProfileData profileData = - new ProfileData( - userData.getId(), - userData.getUsername(), - userData.getBio(), - userData.getImage(), - currentUser != null - && userRelationshipQueryService.isUserFollowing( - currentUser.getId(), userData.getId())); - return Optional.of(profileData); - } - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/TagsQueryService.java b/.framework/java/backend/src/main/java/io/spring/application/TagsQueryService.java deleted file mode 100644 index 12e0790..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/TagsQueryService.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.spring.application; - -import io.spring.infrastructure.mybatis.readservice.TagReadService; -import java.util.List; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@AllArgsConstructor -public class TagsQueryService { - private TagReadService tagReadService; - - public List allTags() { - return tagReadService.all(); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/UserQueryService.java b/.framework/java/backend/src/main/java/io/spring/application/UserQueryService.java deleted file mode 100644 index f0f901a..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/UserQueryService.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.spring.application; - -import io.spring.application.data.UserData; -import io.spring.infrastructure.mybatis.readservice.UserReadService; -import java.util.Optional; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@AllArgsConstructor -public class UserQueryService { - private UserReadService userReadService; - - public Optional findById(String id) { - return Optional.ofNullable(userReadService.findById(id)); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/data/CommentData.java b/.framework/java/backend/src/main/java/io/spring/application/data/CommentData.java deleted file mode 100644 index a31dbf4..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/data/CommentData.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.spring.application.data; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.spring.application.DateTimeCursor; -import io.spring.application.Node; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.joda.time.DateTime; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class CommentData implements Node { - private String id; - private String body; - @JsonIgnore private String itemId; - private DateTime createdAt; - private DateTime updatedAt; - - @JsonProperty("seller") - private ProfileData profileData; - - @Override - public DateTimeCursor getCursor() { - return new DateTimeCursor(createdAt); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/data/ItemData.java b/.framework/java/backend/src/main/java/io/spring/application/data/ItemData.java deleted file mode 100644 index e65f04c..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/data/ItemData.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.spring.application.data; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.spring.application.DateTimeCursor; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.joda.time.DateTime; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class ItemData implements io.spring.application.Node { - private String id; - private String slug; - private String title; - private String description; - private String image; - private boolean favorited; - private int favoritesCount; - private DateTime createdAt; - private DateTime updatedAt; - private List tagList; - - @JsonProperty("seller") - private ProfileData profileData; - - @Override - public DateTimeCursor getCursor() { - return new DateTimeCursor(updatedAt); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/data/ItemDataList.java b/.framework/java/backend/src/main/java/io/spring/application/data/ItemDataList.java deleted file mode 100644 index 983d17c..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/data/ItemDataList.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.spring.application.data; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; -import lombok.Getter; - -@Getter -public class ItemDataList { - @JsonProperty("items") - private final List itemDatas; - - @JsonProperty("itemsCount") - private final int count; - - public ItemDataList(List itemDatas, int count) { - - this.itemDatas = itemDatas; - this.count = count; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/data/ItemFavoriteCount.java b/.framework/java/backend/src/main/java/io/spring/application/data/ItemFavoriteCount.java deleted file mode 100644 index 6d875df..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/data/ItemFavoriteCount.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.spring.application.data; - -import lombok.Value; - -@Value -public class ItemFavoriteCount { - private String id; - private Integer count; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/data/ProfileData.java b/.framework/java/backend/src/main/java/io/spring/application/data/ProfileData.java deleted file mode 100644 index 82ef5f9..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/data/ProfileData.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.spring.application.data; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class ProfileData { - @JsonIgnore private String id; - private String username; - private String bio; - private String image; - private boolean following; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/data/UserData.java b/.framework/java/backend/src/main/java/io/spring/application/data/UserData.java deleted file mode 100644 index c50cc19..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/data/UserData.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.spring.application.data; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@NoArgsConstructor -@AllArgsConstructor -public class UserData { - private String id; - private String email; - private String username; - private String bio; - private String image; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/data/UserWithToken.java b/.framework/java/backend/src/main/java/io/spring/application/data/UserWithToken.java deleted file mode 100644 index eac7f1b..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/data/UserWithToken.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.spring.application.data; - -import lombok.Getter; - -@Getter -public class UserWithToken { - private String email; - private String username; - private String bio; - private String image; - private String token; - - public UserWithToken(UserData userData, String token) { - this.email = userData.getEmail(); - this.username = userData.getUsername(); - this.bio = userData.getBio(); - this.image = userData.getImage(); - this.token = token; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/item/DuplicatedItemConstraint.java b/.framework/java/backend/src/main/java/io/spring/application/item/DuplicatedItemConstraint.java deleted file mode 100644 index 31031a2..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/item/DuplicatedItemConstraint.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.spring.application.item; - -import jakarta.validation.Constraint; -import jakarta.validation.Payload; -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Documented -@Constraint(validatedBy = DuplicatedItemValidator.class) -@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE_USE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface DuplicatedItemConstraint { - String message() default "item name exists"; - - Class[] groups() default {}; - - Class[] payload() default {}; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/item/DuplicatedItemValidator.java b/.framework/java/backend/src/main/java/io/spring/application/item/DuplicatedItemValidator.java deleted file mode 100644 index df7326b..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/item/DuplicatedItemValidator.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.spring.application.item; - -import io.spring.application.ItemQueryService; -import io.spring.core.item.Item; -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import org.springframework.beans.factory.annotation.Autowired; - -class DuplicatedItemValidator implements ConstraintValidator { - - @Autowired private ItemQueryService itemQueryService; - - @Override - public boolean isValid(String value, ConstraintValidatorContext context) { - return !itemQueryService.findBySlug(Item.toSlug(value), null).isPresent(); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/item/ItemCommandService.java b/.framework/java/backend/src/main/java/io/spring/application/item/ItemCommandService.java deleted file mode 100644 index 420c93d..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/item/ItemCommandService.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.spring.application.item; - -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.user.User; -import jakarta.validation.Valid; -import lombok.AllArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -@Service -@Validated -@AllArgsConstructor -public class ItemCommandService { - - private ItemRepository itemRepository; - - public Item createItem(@Valid NewItemParam newItemParam, User creator) { - Item item = - new Item( - newItemParam.getTitle(), - newItemParam.getDescription(), - newItemParam.getImage(), - newItemParam.getTagList(), - creator.getId()); - itemRepository.save(item); - return item; - } - - public Item updateItem(Item item, @Valid UpdateItemParam updateItemParam) { - item.update( - updateItemParam.getTitle(), updateItemParam.getDescription(), updateItemParam.getImage()); - itemRepository.save(item); - return item; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/item/NewItemParam.java b/.framework/java/backend/src/main/java/io/spring/application/item/NewItemParam.java deleted file mode 100644 index aa1ab9a..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/item/NewItemParam.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.spring.application.item; - -import com.fasterxml.jackson.annotation.JsonRootName; -import jakarta.validation.constraints.NotBlank; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@JsonRootName("item") -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class NewItemParam { - @NotBlank(message = "can't be empty") - @DuplicatedItemConstraint - private String title; - - @NotBlank(message = "can't be empty") - private String description; - - private String image; - - private List tagList; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/item/UpdateItemParam.java b/.framework/java/backend/src/main/java/io/spring/application/item/UpdateItemParam.java deleted file mode 100644 index d2f214e..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/item/UpdateItemParam.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.spring.application.item; - -import com.fasterxml.jackson.annotation.JsonRootName; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -@AllArgsConstructor -@JsonRootName("item") -public class UpdateItemParam { - private String title = ""; - private String image = ""; - private String description = ""; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedEmailConstraint.java b/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedEmailConstraint.java deleted file mode 100644 index 8e0f3d5..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedEmailConstraint.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.spring.application.user; - -import jakarta.validation.Constraint; -import jakarta.validation.Payload; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Constraint(validatedBy = DuplicatedEmailValidator.class) -@Retention(RetentionPolicy.RUNTIME) -public @interface DuplicatedEmailConstraint { - String message() default "duplicated email"; - - Class[] groups() default {}; - - Class[] payload() default {}; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedEmailValidator.java b/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedEmailValidator.java deleted file mode 100644 index f754397..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedEmailValidator.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.spring.application.user; - -import io.spring.core.user.UserRepository; -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import org.springframework.beans.factory.annotation.Autowired; - -public class DuplicatedEmailValidator - implements ConstraintValidator { - - @Autowired private UserRepository userRepository; - - @Override - public boolean isValid(String value, ConstraintValidatorContext context) { - return (value == null || value.isEmpty()) || !userRepository.findByEmail(value).isPresent(); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedUsernameConstraint.java b/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedUsernameConstraint.java deleted file mode 100644 index 0e54988..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedUsernameConstraint.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.spring.application.user; - -import jakarta.validation.Constraint; -import jakarta.validation.Payload; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Constraint(validatedBy = DuplicatedUsernameValidator.class) -@Retention(RetentionPolicy.RUNTIME) -@interface DuplicatedUsernameConstraint { - String message() default "duplicated username"; - - Class[] groups() default {}; - - Class[] payload() default {}; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedUsernameValidator.java b/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedUsernameValidator.java deleted file mode 100644 index 4b803bf..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/user/DuplicatedUsernameValidator.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.spring.application.user; - -import io.spring.core.user.UserRepository; -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import org.springframework.beans.factory.annotation.Autowired; - -class DuplicatedUsernameValidator - implements ConstraintValidator { - - @Autowired private UserRepository userRepository; - - @Override - public boolean isValid(String value, ConstraintValidatorContext context) { - return (value == null || value.isEmpty()) || !userRepository.findByUsername(value).isPresent(); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/user/RegisterParam.java b/.framework/java/backend/src/main/java/io/spring/application/user/RegisterParam.java deleted file mode 100644 index d629268..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/user/RegisterParam.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.spring.application.user; - -import com.fasterxml.jackson.annotation.JsonRootName; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@JsonRootName("user") -@AllArgsConstructor -@NoArgsConstructor -public class RegisterParam { - @NotBlank(message = "can't be empty") - @Email(message = "should be an email") - @DuplicatedEmailConstraint - private String email; - - @NotBlank(message = "can't be empty") - @DuplicatedUsernameConstraint - private String username; - - @NotBlank(message = "can't be empty") - private String password; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/user/UpdateUserCommand.java b/.framework/java/backend/src/main/java/io/spring/application/user/UpdateUserCommand.java deleted file mode 100644 index 9df5230..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/user/UpdateUserCommand.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.spring.application.user; - -import io.spring.core.user.User; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -@UpdateUserConstraint -public class UpdateUserCommand { - - private User targetUser; - private UpdateUserParam param; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/user/UpdateUserParam.java b/.framework/java/backend/src/main/java/io/spring/application/user/UpdateUserParam.java deleted file mode 100644 index f0263a0..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/user/UpdateUserParam.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.spring.application.user; - -import com.fasterxml.jackson.annotation.JsonRootName; -import jakarta.validation.constraints.Email; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@JsonRootName("user") -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class UpdateUserParam { - - @Builder.Default - @Email(message = "should be an email") - private String email = ""; - - @Builder.Default private String password = ""; - @Builder.Default private String username = ""; - @Builder.Default private String bio = ""; - @Builder.Default private String image = ""; -} diff --git a/.framework/java/backend/src/main/java/io/spring/application/user/UserService.java b/.framework/java/backend/src/main/java/io/spring/application/user/UserService.java deleted file mode 100644 index 7772b32..0000000 --- a/.framework/java/backend/src/main/java/io/spring/application/user/UserService.java +++ /dev/null @@ -1,106 +0,0 @@ -package io.spring.application.user; - -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import jakarta.validation.Constraint; -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; -import jakarta.validation.Valid; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -@Service -@Validated -public class UserService { - private UserRepository userRepository; - private String defaultImage; - private PasswordEncoder passwordEncoder; - - @Autowired - public UserService( - UserRepository userRepository, - @Value("${image.default}") String defaultImage, - PasswordEncoder passwordEncoder) { - this.userRepository = userRepository; - this.defaultImage = defaultImage; - this.passwordEncoder = passwordEncoder; - } - - public User createUser(@Valid RegisterParam registerParam) { - User user = - new User( - registerParam.getEmail(), - registerParam.getUsername(), - passwordEncoder.encode(registerParam.getPassword()), - "", - defaultImage); - userRepository.save(user); - return user; - } - - public void updateUser(@Valid UpdateUserCommand command) { - User user = command.getTargetUser(); - UpdateUserParam updateUserParam = command.getParam(); - user.update( - updateUserParam.getEmail(), - updateUserParam.getUsername(), - updateUserParam.getPassword(), - updateUserParam.getBio(), - updateUserParam.getImage()); - userRepository.save(user); - } -} - -@Constraint(validatedBy = UpdateUserValidator.class) -@Retention(RetentionPolicy.RUNTIME) -@interface UpdateUserConstraint { - - String message() default "invalid update param"; - - Class[] groups() default {}; - - Class[] payload() default {}; -} - -class UpdateUserValidator implements ConstraintValidator { - - @Autowired private UserRepository userRepository; - - @Override - public boolean isValid(UpdateUserCommand value, ConstraintValidatorContext context) { - String inputEmail = value.getParam().getEmail(); - String inputUsername = value.getParam().getUsername(); - final User targetUser = value.getTargetUser(); - - boolean isEmailValid = - userRepository.findByEmail(inputEmail).map(user -> user.equals(targetUser)).orElse(true); - boolean isUsernameValid = - userRepository - .findByUsername(inputUsername) - .map(user -> user.equals(targetUser)) - .orElse(true); - if (isEmailValid && isUsernameValid) { - return true; - } else { - context.disableDefaultConstraintViolation(); - if (!isEmailValid) { - context - .buildConstraintViolationWithTemplate("email already exist") - .addPropertyNode("email") - .addConstraintViolation(); - } - if (!isUsernameValid) { - context - .buildConstraintViolationWithTemplate("username already exist") - .addPropertyNode("username") - .addConstraintViolation(); - } - return false; - } - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/comment/Comment.java b/.framework/java/backend/src/main/java/io/spring/core/comment/Comment.java deleted file mode 100644 index ecfcfa1..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/comment/Comment.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.spring.core.comment; - -import java.util.UUID; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.joda.time.DateTime; - -@Getter -@NoArgsConstructor -@EqualsAndHashCode(of = "id") -public class Comment { - private String id; - private String body; - private String sellerId; - private String itemId; - private DateTime createdAt; - - public Comment(String body, String sellerId, String itemId) { - this.id = UUID.randomUUID().toString(); - this.body = body; - this.sellerId = sellerId; - this.itemId = itemId; - this.createdAt = new DateTime(); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/comment/CommentRepository.java b/.framework/java/backend/src/main/java/io/spring/core/comment/CommentRepository.java deleted file mode 100644 index ad3ab07..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/comment/CommentRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.spring.core.comment; - -import java.util.Optional; - -public interface CommentRepository { - void save(Comment comment); - - Optional findById(String itemId, String id); - - void remove(Comment comment); -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/favorite/ItemFavorite.java b/.framework/java/backend/src/main/java/io/spring/core/favorite/ItemFavorite.java deleted file mode 100644 index 39ce24d..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/favorite/ItemFavorite.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.spring.core.favorite; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@NoArgsConstructor -@Getter -@EqualsAndHashCode -public class ItemFavorite { - private String itemId; - private String userId; - - public ItemFavorite(String itemId, String userId) { - this.itemId = itemId; - this.userId = userId; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/favorite/ItemFavoriteRepository.java b/.framework/java/backend/src/main/java/io/spring/core/favorite/ItemFavoriteRepository.java deleted file mode 100644 index 61db705..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/favorite/ItemFavoriteRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.spring.core.favorite; - -import java.util.Optional; - -public interface ItemFavoriteRepository { - void save(ItemFavorite itemFavorite); - - Optional find(String itemId, String userId); - - void remove(ItemFavorite favorite); -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/item/Item.java b/.framework/java/backend/src/main/java/io/spring/core/item/Item.java deleted file mode 100644 index 6b39667..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/item/Item.java +++ /dev/null @@ -1,74 +0,0 @@ -package io.spring.core.item; - -import io.spring.Util; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Collectors; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.joda.time.DateTime; - -@Getter -@NoArgsConstructor -@EqualsAndHashCode(of = {"id"}) -public class Item { - private String sellerId; - private String id; - private String slug; - private String title; - private String description; - private String image; - private List tags; - private DateTime createdAt; - private DateTime updatedAt; - - public Item( - String title, String description, String image, List tagList, String sellerId) { - this(title, description, image, tagList, sellerId, new DateTime()); - } - - public Item( - String title, - String description, - String image, - List tagList, - String sellerId, - DateTime createdAt) { - this.id = UUID.randomUUID().toString(); - this.slug = toSlug(title); - this.title = title; - this.description = description; - this.image = image; - this.tags = - Optional.ofNullable(tagList) - .map(list -> new HashSet<>(list).stream().map(Tag::new).collect(Collectors.toList())) - .orElse(new ArrayList()); - - this.sellerId = sellerId; - this.createdAt = createdAt; - this.updatedAt = createdAt; - } - - public void update(String title, String description, String image) { - if (!Util.isEmpty(title)) { - this.title = title; - this.updatedAt = new DateTime(); - } - if (!Util.isEmpty(description)) { - this.description = description; - this.updatedAt = new DateTime(); - } - if (!Util.isEmpty(image)) { - this.image = image; - this.updatedAt = new DateTime(); - } - } - - public static String toSlug(String title) { - return title.toLowerCase().replaceAll("[\\&|[\\uFE30-\\uFFA0]|\\’|\\”|\\s\\?\\,\\.]+", "-"); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/item/ItemRepository.java b/.framework/java/backend/src/main/java/io/spring/core/item/ItemRepository.java deleted file mode 100644 index 6dea2ea..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/item/ItemRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.spring.core.item; - -import java.util.Optional; - -public interface ItemRepository { - - void save(Item item); - - Optional findById(String id); - - Optional findBySlug(String slug); - - void remove(Item item); -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/item/Tag.java b/.framework/java/backend/src/main/java/io/spring/core/item/Tag.java deleted file mode 100644 index 7edd313..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/item/Tag.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.spring.core.item; - -import java.util.UUID; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import org.joda.time.DateTime; - -@NoArgsConstructor -@Data -@EqualsAndHashCode(of = "name") -public class Tag { - private String id; - private String name; - private DateTime createdAt; - - public Tag(String name) { - this.id = UUID.randomUUID().toString(); - this.name = name; - this.createdAt = new DateTime(); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/service/AuthorizationService.java b/.framework/java/backend/src/main/java/io/spring/core/service/AuthorizationService.java deleted file mode 100644 index 080e742..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/service/AuthorizationService.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.spring.core.service; - -import io.spring.core.comment.Comment; -import io.spring.core.item.Item; -import io.spring.core.user.User; - -public class AuthorizationService { - public static boolean canWriteItem(User user, Item item) { - return user.getId().equals(item.getSellerId()); - } - - public static boolean canWriteComment(User user, Item item, Comment comment) { - return user.getId().equals(item.getSellerId()) || user.getId().equals(comment.getSellerId()); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/service/JwtService.java b/.framework/java/backend/src/main/java/io/spring/core/service/JwtService.java deleted file mode 100644 index d143076..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/service/JwtService.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.spring.core.service; - -import io.spring.core.user.User; -import java.util.Optional; -import org.springframework.stereotype.Service; - -@Service -public interface JwtService { - String toToken(User user); - - Optional getSubFromToken(String token); -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/user/FollowRelation.java b/.framework/java/backend/src/main/java/io/spring/core/user/FollowRelation.java deleted file mode 100644 index 7d7b538..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/user/FollowRelation.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.spring.core.user; - -import lombok.Data; -import lombok.NoArgsConstructor; - -@NoArgsConstructor -@Data -public class FollowRelation { - private String userId; - private String targetId; - - public FollowRelation(String userId, String targetId) { - - this.userId = userId; - this.targetId = targetId; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/user/User.java b/.framework/java/backend/src/main/java/io/spring/core/user/User.java deleted file mode 100644 index 3044d50..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/user/User.java +++ /dev/null @@ -1,50 +0,0 @@ -package io.spring.core.user; - -import io.spring.Util; -import java.util.UUID; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -@EqualsAndHashCode(of = {"id"}) -public class User { - private String id; - private String email; - private String username; - private String password; - private String bio; - private String image; - - public User(String email, String username, String password, String bio, String image) { - this.id = UUID.randomUUID().toString(); - this.email = email; - this.username = username; - this.password = password; - this.bio = bio; - this.image = image; - } - - public void update(String email, String username, String password, String bio, String image) { - if (!Util.isEmpty(email)) { - this.email = email; - } - - if (!Util.isEmpty(username)) { - this.username = username; - } - - if (!Util.isEmpty(password)) { - this.password = password; - } - - if (!Util.isEmpty(bio)) { - this.bio = bio; - } - - if (!Util.isEmpty(image)) { - this.image = image; - } - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/core/user/UserRepository.java b/.framework/java/backend/src/main/java/io/spring/core/user/UserRepository.java deleted file mode 100644 index f52c772..0000000 --- a/.framework/java/backend/src/main/java/io/spring/core/user/UserRepository.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.spring.core.user; - -import java.util.Optional; -import org.springframework.stereotype.Repository; - -@Repository -public interface UserRepository { - void save(User user); - - Optional findById(String id); - - Optional findByUsername(String username); - - Optional findByEmail(String email); - - void saveRelation(FollowRelation followRelation); - - Optional findRelation(String userId, String targetId); - - void removeRelation(FollowRelation followRelation); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/DateTimeHandler.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/DateTimeHandler.java deleted file mode 100644 index 19323e5..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/DateTimeHandler.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.spring.infrastructure.mybatis; - -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.Calendar; -import java.util.TimeZone; -import org.apache.ibatis.type.JdbcType; -import org.apache.ibatis.type.MappedTypes; -import org.apache.ibatis.type.TypeHandler; -import org.joda.time.DateTime; - -@MappedTypes(DateTime.class) -public class DateTimeHandler implements TypeHandler { - - private static final Calendar UTC_CALENDAR = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - - @Override - public void setParameter(PreparedStatement ps, int i, DateTime parameter, JdbcType jdbcType) - throws SQLException { - ps.setTimestamp( - i, parameter != null ? new Timestamp(parameter.getMillis()) : null, UTC_CALENDAR); - } - - @Override - public DateTime getResult(ResultSet rs, String columnName) throws SQLException { - Timestamp timestamp = rs.getTimestamp(columnName, UTC_CALENDAR); - return timestamp != null ? new DateTime(timestamp.getTime()) : null; - } - - @Override - public DateTime getResult(ResultSet rs, int columnIndex) throws SQLException { - Timestamp timestamp = rs.getTimestamp(columnIndex, UTC_CALENDAR); - return timestamp != null ? new DateTime(timestamp.getTime()) : null; - } - - @Override - public DateTime getResult(CallableStatement cs, int columnIndex) throws SQLException { - Timestamp ts = cs.getTimestamp(columnIndex, UTC_CALENDAR); - return ts != null ? new DateTime(ts.getTime()) : null; - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/CommentMapper.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/CommentMapper.java deleted file mode 100644 index 5b7c3cc..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/CommentMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.spring.infrastructure.mybatis.mapper; - -import io.spring.core.comment.Comment; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -@Mapper -public interface CommentMapper { - void insert(@Param("comment") Comment comment); - - Comment findById(@Param("itemId") String itemId, @Param("id") String id); - - void delete(@Param("id") String id); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/ItemFavoriteMapper.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/ItemFavoriteMapper.java deleted file mode 100644 index 4ff92dc..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/ItemFavoriteMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.spring.infrastructure.mybatis.mapper; - -import io.spring.core.favorite.ItemFavorite; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -@Mapper -public interface ItemFavoriteMapper { - ItemFavorite find(@Param("itemId") String itemId, @Param("userId") String userId); - - void insert(@Param("itemFavorite") ItemFavorite itemFavorite); - - void delete(@Param("favorite") ItemFavorite favorite); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/ItemMapper.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/ItemMapper.java deleted file mode 100644 index 8dd8696..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/ItemMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.spring.infrastructure.mybatis.mapper; - -import io.spring.core.item.Item; -import io.spring.core.item.Tag; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.joda.time.DateTime; - -@Mapper -public interface ItemMapper { - void insert(@Param("item") Item item); - - Item findById(@Param("id") String id); - - Tag findTag(@Param("tagName") String tagName); - - void insertTag(@Param("tag") Tag tag); - - void insertItemTagRelation( - @Param("itemId") String itemId, - @Param("tagId") String tagId, - @Param("createdAt") DateTime createdAt); - - Item findBySlug(@Param("slug") String slug); - - void update(@Param("item") Item item); - - void delete(@Param("id") String id); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/UserMapper.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/UserMapper.java deleted file mode 100644 index 54f36c7..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/mapper/UserMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.spring.infrastructure.mybatis.mapper; - -import io.spring.core.user.FollowRelation; -import io.spring.core.user.User; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -@Mapper -public interface UserMapper { - void insert(@Param("user") User user); - - User findByUsername(@Param("username") String username); - - User findByEmail(@Param("email") String email); - - User findById(@Param("id") String id); - - void update(@Param("user") User user); - - FollowRelation findRelation(@Param("userId") String userId, @Param("targetId") String targetId); - - void saveRelation(@Param("followRelation") FollowRelation followRelation); - - void deleteRelation(@Param("followRelation") FollowRelation followRelation); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java deleted file mode 100644 index 2c27b7c..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.spring.infrastructure.mybatis.readservice; - -import io.spring.application.CursorPageParameter; -import io.spring.application.data.CommentData; -import java.util.List; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.joda.time.DateTime; - -@Mapper -public interface CommentReadService { - CommentData findById(@Param("id") String id); - - List findByItemId(@Param("itemId") String itemId); - - List findByItemIdWithCursor( - @Param("itemId") String itemId, @Param("page") CursorPageParameter page); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/ItemFavoritesReadService.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/ItemFavoritesReadService.java deleted file mode 100644 index c913a23..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/ItemFavoritesReadService.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.spring.infrastructure.mybatis.readservice; - -import io.spring.application.data.ItemFavoriteCount; -import io.spring.core.user.User; -import java.util.List; -import java.util.Set; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -@Mapper -public interface ItemFavoritesReadService { - boolean isUserFavorite(@Param("userId") String userId, @Param("itemId") String itemId); - - int itemFavoriteCount(@Param("itemId") String itemId); - - List itemsFavoriteCount(@Param("ids") List ids); - - Set userFavorites(@Param("ids") List ids, @Param("currentUser") User currentUser); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/ItemReadService.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/ItemReadService.java deleted file mode 100644 index e8198f9..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/ItemReadService.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.spring.infrastructure.mybatis.readservice; - -import io.spring.application.CursorPageParameter; -import io.spring.application.Page; -import io.spring.application.data.ItemData; -import java.util.List; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -@Mapper -public interface ItemReadService { - ItemData findById(@Param("id") String id); - - ItemData findBySlug(@Param("slug") String slug); - - List queryItems( - @Param("tag") String tag, - @Param("seller") String seller, - @Param("favoritedBy") String favoritedBy, - @Param("page") Page page); - - int countItem( - @Param("tag") String tag, - @Param("seller") String seller, - @Param("favoritedBy") String favoritedBy); - - List findItems(@Param("itemIds") List itemIds); - - List findItemsOfSellers( - @Param("sellers") List authors, @Param("page") Page page); - - List findItemsOfSellersWithCursor( - @Param("sellers") List authors, @Param("page") CursorPageParameter page); - - int countFeedSize(@Param("sellers") List sellers); - - List findItemsWithCursor( - @Param("tag") String tag, - @Param("seller") String seller, - @Param("favoritedBy") String favoritedBy, - @Param("page") CursorPageParameter page); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/TagReadService.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/TagReadService.java deleted file mode 100644 index 8737687..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/TagReadService.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.spring.infrastructure.mybatis.readservice; - -import java.util.List; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface TagReadService { - List all(); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/UserReadService.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/UserReadService.java deleted file mode 100644 index ae25a48..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/UserReadService.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.spring.infrastructure.mybatis.readservice; - -import io.spring.application.data.UserData; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -@Mapper -public interface UserReadService { - - UserData findByUsername(@Param("username") String username); - - UserData findById(@Param("id") String id); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/UserRelationshipQueryService.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/UserRelationshipQueryService.java deleted file mode 100644 index 4a8b230..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/mybatis/readservice/UserRelationshipQueryService.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.spring.infrastructure.mybatis.readservice; - -import java.util.List; -import java.util.Set; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -@Mapper -public interface UserRelationshipQueryService { - boolean isUserFollowing( - @Param("userId") String userId, @Param("anotherUserId") String anotherUserId); - - Set followingSellers(@Param("userId") String userId, @Param("ids") List ids); - - List followedUsers(@Param("userId") String userId); -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisCommentRepository.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisCommentRepository.java deleted file mode 100644 index e0d33e3..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisCommentRepository.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.spring.infrastructure.repository; - -import io.spring.core.comment.Comment; -import io.spring.core.comment.CommentRepository; -import io.spring.infrastructure.mybatis.mapper.CommentMapper; -import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class MyBatisCommentRepository implements CommentRepository { - private CommentMapper commentMapper; - - @Autowired - public MyBatisCommentRepository(CommentMapper commentMapper) { - this.commentMapper = commentMapper; - } - - @Override - public void save(Comment comment) { - commentMapper.insert(comment); - } - - @Override - public Optional findById(String itemId, String id) { - return Optional.ofNullable(commentMapper.findById(itemId, id)); - } - - @Override - public void remove(Comment comment) { - commentMapper.delete(comment.getId()); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisItemFavoriteRepository.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisItemFavoriteRepository.java deleted file mode 100644 index 138bc99..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisItemFavoriteRepository.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.spring.infrastructure.repository; - -import io.spring.core.favorite.ItemFavorite; -import io.spring.core.favorite.ItemFavoriteRepository; -import io.spring.infrastructure.mybatis.mapper.ItemFavoriteMapper; -import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Repository; - -@Repository -public class MyBatisItemFavoriteRepository implements ItemFavoriteRepository { - private ItemFavoriteMapper mapper; - - @Autowired - public MyBatisItemFavoriteRepository(ItemFavoriteMapper mapper) { - this.mapper = mapper; - } - - @Override - public void save(ItemFavorite itemFavorite) { - if (mapper.find(itemFavorite.getItemId(), itemFavorite.getUserId()) == null) { - mapper.insert(itemFavorite); - } - } - - @Override - public Optional find(String itemId, String userId) { - return Optional.ofNullable(mapper.find(itemId, userId)); - } - - @Override - public void remove(ItemFavorite favorite) { - mapper.delete(favorite); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisItemRepository.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisItemRepository.java deleted file mode 100644 index 1f30b58..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisItemRepository.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.spring.infrastructure.repository; - -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.item.Tag; -import io.spring.infrastructure.mybatis.mapper.ItemMapper; -import java.util.Optional; -import org.joda.time.DateTime; -import org.springframework.stereotype.Repository; -import org.springframework.transaction.annotation.Transactional; - -@Repository -public class MyBatisItemRepository implements ItemRepository { - private ItemMapper itemMapper; - - public MyBatisItemRepository(ItemMapper itemMapper) { - this.itemMapper = itemMapper; - } - - @Override - @Transactional - public void save(Item item) { - if (itemMapper.findById(item.getId()) == null) { - createNew(item); - } else { - itemMapper.update(item); - } - } - - private void createNew(Item item) { - for (Tag tag : item.getTags()) { - Tag targetTag = - Optional.ofNullable(itemMapper.findTag(tag.getName())) - .orElseGet( - () -> { - itemMapper.insertTag(tag); - return tag; - }); - - itemMapper.insertItemTagRelation(item.getId(), targetTag.getId(), new DateTime()); - } - itemMapper.insert(item); - } - - @Override - public Optional findById(String id) { - return Optional.ofNullable(itemMapper.findById(id)); - } - - @Override - public Optional findBySlug(String slug) { - return Optional.ofNullable(itemMapper.findBySlug(slug)); - } - - @Override - public void remove(Item item) { - itemMapper.delete(item.getId()); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisUserRepository.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisUserRepository.java deleted file mode 100644 index 3c24dd5..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/repository/MyBatisUserRepository.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.spring.infrastructure.repository; - -import io.spring.core.user.FollowRelation; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.mybatis.mapper.UserMapper; -import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Repository; - -@Repository -public class MyBatisUserRepository implements UserRepository { - private final UserMapper userMapper; - - @Autowired - public MyBatisUserRepository(UserMapper userMapper) { - this.userMapper = userMapper; - } - - @Override - public void save(User user) { - if (userMapper.findById(user.getId()) == null) { - userMapper.insert(user); - } else { - userMapper.update(user); - } - } - - @Override - public Optional findById(String id) { - return Optional.ofNullable(userMapper.findById(id)); - } - - @Override - public Optional findByUsername(String username) { - return Optional.ofNullable(userMapper.findByUsername(username)); - } - - @Override - public Optional findByEmail(String email) { - return Optional.ofNullable(userMapper.findByEmail(email)); - } - - @Override - public void saveRelation(FollowRelation followRelation) { - if (!findRelation(followRelation.getUserId(), followRelation.getTargetId()).isPresent()) { - userMapper.saveRelation(followRelation); - } - } - - @Override - public Optional findRelation(String userId, String targetId) { - return Optional.ofNullable(userMapper.findRelation(userId, targetId)); - } - - @Override - public void removeRelation(FollowRelation followRelation) { - userMapper.deleteRelation(followRelation); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/service/DefaultJwtService.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/service/DefaultJwtService.java deleted file mode 100644 index 515d661..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/service/DefaultJwtService.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.spring.infrastructure.service; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jws; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import io.spring.core.service.JwtService; -import io.spring.core.user.User; -import java.util.Date; -import java.util.Optional; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -@Component -public class DefaultJwtService implements JwtService { - private final SecretKey signingKey; - private final SignatureAlgorithm signatureAlgorithm; - private int sessionTime; - - @Autowired - public DefaultJwtService( - @Value("${jwt.secret}") String secret, @Value("${jwt.sessionTime}") int sessionTime) { - this.sessionTime = sessionTime; - signatureAlgorithm = SignatureAlgorithm.HS512; - this.signingKey = new SecretKeySpec(secret.getBytes(), signatureAlgorithm.getJcaName()); - } - - @Override - public String toToken(User user) { - return Jwts.builder() - .setSubject(user.getId()) - .setExpiration(expireTimeFromNow()) - .signWith(signingKey) - .compact(); - } - - @Override - public Optional getSubFromToken(String token) { - try { - Jws claimsJws = - Jwts.parserBuilder().setSigningKey(signingKey).build().parseClaimsJws(token); - return Optional.ofNullable(claimsJws.getBody().getSubject()); - } catch (Exception e) { - return Optional.empty(); - } - } - - private Date expireTimeFromNow() { - return new Date(System.currentTimeMillis() + sessionTime * 1000L); - } -} diff --git a/.framework/java/backend/src/main/java/io/spring/infrastructure/service/SendEventService.java b/.framework/java/backend/src/main/java/io/spring/infrastructure/service/SendEventService.java deleted file mode 100644 index 401704d..0000000 --- a/.framework/java/backend/src/main/java/io/spring/infrastructure/service/SendEventService.java +++ /dev/null @@ -1,77 +0,0 @@ -package io.spring.infrastructure.service; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.Map; -import java.util.Objects; -import okhttp3.*; - -public class SendEventService { - - private static final String PATH_TO_WILCO_ID = "../.wilco"; - private static final String BASE_URL = - Objects.requireNonNullElse(System.getenv("ENGINE_BASE_URL"), "https://engine.wilco.gg"); - private static String wilcoId; - - private final OkHttpClient client; - private final ObjectMapper objectMapper; - - public SendEventService() { - this.client = new OkHttpClient(); - this.objectMapper = new ObjectMapper(); - this.wilcoId = System.getenv("WILCO_ID"); - - if (wilcoId == null && doesFileExist(PATH_TO_WILCO_ID)) { - wilcoId = readFile(PATH_TO_WILCO_ID); - } - } - - public String sendEvent(String event, Map metadata) { - MediaType mediaType = MediaType.parse("application/json"); - - JsonNode metadataNode = objectMapper.valueToTree(metadata); - - ObjectNode data = objectMapper.createObjectNode(); - data.put("event", event); - data.set("metadata", metadataNode); - - RequestBody requestBody = RequestBody.create(mediaType, data.toString()); - - Request request = - new Request.Builder() - .url(BASE_URL + "/users/" + wilcoId + "/event") - .post(requestBody) - .addHeader("Content-type", "application/json") - .build(); - - try { - Response response = client.newCall(request).execute(); - return response.body().string(); - } catch (IOException e) { - e.printStackTrace(); - System.err.println("Failed to send event " + event + " to Wilco engine"); - return null; - } - } - - private static boolean doesFileExist(String filePath) { - return Objects.requireNonNull(new java.io.File(filePath).exists()); - } - - private static String readFile(String filePath) { - StringBuilder content = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { - String line; - while ((line = reader.readLine()) != null) { - content.append(line); - } - } catch (IOException e) { - e.printStackTrace(); - } - return content.toString(); - } -} diff --git a/.framework/java/backend/src/main/resources/application-test.properties b/.framework/java/backend/src/main/resources/application-test.properties deleted file mode 100644 index 612f0b6..0000000 --- a/.framework/java/backend/src/main/resources/application-test.properties +++ /dev/null @@ -1,4 +0,0 @@ -spring.datasource.url=jdbc:h2:mem:testdb;MODE=PostgreSQL -spring.datasource.driverClassName=org.h2.Driver -spring.datasource.username=sa -spring.datasource.password=password diff --git a/.framework/java/backend/src/main/resources/application.properties b/.framework/java/backend/src/main/resources/application.properties deleted file mode 100644 index 4319b5e..0000000 --- a/.framework/java/backend/src/main/resources/application.properties +++ /dev/null @@ -1,24 +0,0 @@ -spring.datasource.url=jdbc:postgresql://postgres-java:5432/anythink-market -spring.datasource.username=postgres -spring.datasource.password= -spring.datasource.driver-class-name=org.postgresql.Driver -spring.jackson.deserialization.UNWRAP_ROOT_VALUE=true - -image.default=https://static.productionready.io/images/smiley-cyrus.jpg - -jwt.secret=nRvyYC4soFxBdZ-F-5Nnzz5USXstR1YylsTd-mA0aKtI9HUlriGrtkf-TiuDapkLiUCogO3JOK7kwZisrHp6wA -jwt.sessionTime=86400 - -mybatis.configuration.cache-enabled=true -mybatis.configuration.default-statement-timeout=3000 -mybatis.configuration.map-underscore-to-camel-case=true -mybatis.configuration.use-generated-keys=true -mybatis.type-handlers-package=io.spring.infrastructure.mybatis -mybatis.mapper-locations=mapper/*.xml - -logging.level.io.spring.infrastructure.mybatis.readservice.ItemReadService=DEBUG -logging.level.io.spring.infrastructure.mybatis.mapper=DEBUG - -server.port=3000 - -management.endpoints.web.base-path=/ diff --git a/.framework/java/backend/src/main/resources/db/migration/V1__create_tables.sql b/.framework/java/backend/src/main/resources/db/migration/V1__create_tables.sql deleted file mode 100644 index 1a5b612..0000000 --- a/.framework/java/backend/src/main/resources/db/migration/V1__create_tables.sql +++ /dev/null @@ -1,50 +0,0 @@ -create table users ( - id varchar(255) primary key, - username varchar(255) UNIQUE, - password varchar(255), - email varchar(255) UNIQUE, - bio text, - image varchar(511) -); - -create table items ( - id varchar(255) primary key, - seller_id varchar(255), - slug varchar(255) UNIQUE, - title varchar(255), - description text, - image text, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -create table item_favorites ( - item_id varchar(255) not null, - user_id varchar(255) not null, - primary key(item_id, user_id) -); - -create table follows ( - user_id varchar(255) not null, - follow_id varchar(255) not null -); - -create table tags ( - id varchar(255) primary key, - name varchar(255) not null -); - -create table item_tags ( - item_id varchar(255) not null, - tag_id varchar(255) not null, - created_at TIMESTAMP not null -); - -create table comments ( - id varchar(255) primary key, - body text, - item_id varchar(255), - seller_id varchar(255), - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); diff --git a/.framework/java/backend/src/main/resources/mapper/CommentMapper.xml b/.framework/java/backend/src/main/resources/mapper/CommentMapper.xml deleted file mode 100644 index c715304..0000000 --- a/.framework/java/backend/src/main/resources/mapper/CommentMapper.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - insert into comments(id, body, seller_id, item_id, created_at, updated_at) - values ( - #{comment.id}, - #{comment.body}, - #{comment.sellerId}, - #{comment.itemId}, - #{comment.createdAt}, - #{comment.createdAt} - ) - - - delete from comments where id = #{id} - - - - - - - - - - diff --git a/.framework/java/backend/src/main/resources/mapper/CommentReadService.xml b/.framework/java/backend/src/main/resources/mapper/CommentReadService.xml deleted file mode 100644 index cebb77e..0000000 --- a/.framework/java/backend/src/main/resources/mapper/CommentReadService.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - SELECT - C.id commentId, - C.body commentBody, - C.created_at commentCreatedAt, - C.item_id commentItemId, - - from comments C - left join users U - on C.seller_id = U.id - - - - - - diff --git a/.framework/java/backend/src/main/resources/mapper/ItemFavoriteMapper.xml b/.framework/java/backend/src/main/resources/mapper/ItemFavoriteMapper.xml deleted file mode 100644 index 5fbba91..0000000 --- a/.framework/java/backend/src/main/resources/mapper/ItemFavoriteMapper.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - insert into item_favorites (item_id, user_id) values (#{itemFavorite.itemId}, #{itemFavorite.userId}) - - - delete from item_favorites where item_id = #{favorite.itemId} and user_id = #{favorite.userId} - - - - - - - diff --git a/.framework/java/backend/src/main/resources/mapper/ItemFavoritesReadService.xml b/.framework/java/backend/src/main/resources/mapper/ItemFavoritesReadService.xml deleted file mode 100644 index 16a673f..0000000 --- a/.framework/java/backend/src/main/resources/mapper/ItemFavoritesReadService.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - diff --git a/.framework/java/backend/src/main/resources/mapper/ItemMapper.xml b/.framework/java/backend/src/main/resources/mapper/ItemMapper.xml deleted file mode 100644 index f907a17..0000000 --- a/.framework/java/backend/src/main/resources/mapper/ItemMapper.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - insert into items(id, slug, title, description, image, seller_id, created_at, updated_at) - values( - #{item.id}, - #{item.slug}, - #{item.title}, - #{item.description}, - #{item.image}, - #{item.sellerId}, - #{item.createdAt}, - #{item.updatedAt}) - - - insert into tags (id, name) values (#{tag.id}, #{tag.name}) - - - insert into item_tags (item_id, tag_id, created_at) values(#{itemId}, #{tagId}, #{createdAt}) - - - update items - - title = #{item.title}, - slug = #{item.slug}, - description = #{item.description}, - image = #{item.image} - - where id = #{item.id} - - - delete from items where id = #{id} - - - select - A.id itemId, - A.slug itemSlug, - A.title itemTitle, - A.description itemDescription, - A.image itemImage, - A.seller_id itemSellerId, - A.created_at itemCreatedAt, - A.updated_at itemUpdatedAt, - T.id tagId, - T.name tagName - from items A - left join item_tags AT on A.id = AT.item_id - left join tags T on T.id = AT.tag_id - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.framework/java/backend/src/main/resources/mapper/ItemReadService.xml b/.framework/java/backend/src/main/resources/mapper/ItemReadService.xml deleted file mode 100644 index 2740017..0000000 --- a/.framework/java/backend/src/main/resources/mapper/ItemReadService.xml +++ /dev/null @@ -1,160 +0,0 @@ - - - - - U.id userId, - U.username userUsername, - U.bio userBio, - U.image userImage - - - select - A.id itemId, - A.slug itemSlug, - A.title itemTitle, - A.description itemDescription, - A.image itemImage, - A.created_at itemCreatedAt, - A.updated_at itemUpdatedAt, - T.name tagName, - - from - items A - left join item_tags AT on A.id = AT.item_id - left join tags T on T.id = AT.tag_id - left join users U on U.id = A.seller_id - - - select - DISTINCT(A.id) itemId, A.created_at - from - items A - left join item_tags AT on A.id = AT.item_id - left join tags T on T.id = AT.tag_id - left join item_favorites AF on AF.item_id = A.id - left join users AU on AU.id = A.seller_id - left join users AFU on AFU.id = AF.user_id - - - - - - - - - - - - - - - - diff --git a/.framework/java/backend/src/main/resources/mapper/TagReadService.xml b/.framework/java/backend/src/main/resources/mapper/TagReadService.xml deleted file mode 100644 index 0e8ceef..0000000 --- a/.framework/java/backend/src/main/resources/mapper/TagReadService.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.framework/java/backend/src/main/resources/mapper/TransferData.xml b/.framework/java/backend/src/main/resources/mapper/TransferData.xml deleted file mode 100644 index 45bcd8d..0000000 --- a/.framework/java/backend/src/main/resources/mapper/TransferData.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.framework/java/backend/src/main/resources/mapper/UserMapper.xml b/.framework/java/backend/src/main/resources/mapper/UserMapper.xml deleted file mode 100644 index 08e89b2..0000000 --- a/.framework/java/backend/src/main/resources/mapper/UserMapper.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - insert into users (id, username, email, password, bio, image) values( - #{user.id}, - #{user.username}, - #{user.email}, - #{user.password}, - #{user.bio}, - #{user.image} - ) - - - insert into follows(user_id, follow_id) values (#{followRelation.userId}, #{followRelation.targetId}) - - - update users - - username = #{user.username}, - email = #{user.email}, - password = #{user.password}, - bio = #{user.bio}, - image = #{user.image} - - where id = #{user.id} - - - delete from follows where user_id = #{followRelation.userId} and follow_id = #{followRelation.targetId} - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.framework/java/backend/src/main/resources/mapper/UserReadService.xml b/.framework/java/backend/src/main/resources/mapper/UserReadService.xml deleted file mode 100644 index edf53c1..0000000 --- a/.framework/java/backend/src/main/resources/mapper/UserReadService.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.framework/java/backend/src/main/resources/mapper/UserRelationshipQueryService.xml b/.framework/java/backend/src/main/resources/mapper/UserRelationshipQueryService.xml deleted file mode 100644 index 8bed754..0000000 --- a/.framework/java/backend/src/main/resources/mapper/UserRelationshipQueryService.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/.framework/java/backend/src/test/java/io/spring/AnythinkMarketApplicationTests.java b/.framework/java/backend/src/test/java/io/spring/AnythinkMarketApplicationTests.java deleted file mode 100644 index cd81530..0000000 --- a/.framework/java/backend/src/test/java/io/spring/AnythinkMarketApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.spring; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; - -@ActiveProfiles("test") -@SpringBootTest() -public class AnythinkMarketApplicationTests { - - @Test - public void contextLoads() {} -} diff --git a/.framework/java/backend/src/test/java/io/spring/TestHelper.java b/.framework/java/backend/src/test/java/io/spring/TestHelper.java deleted file mode 100644 index 3dd29f2..0000000 --- a/.framework/java/backend/src/test/java/io/spring/TestHelper.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.spring; - -import io.spring.application.data.ItemData; -import io.spring.application.data.ProfileData; -import io.spring.core.item.Item; -import io.spring.core.user.User; -import java.util.ArrayList; -import java.util.Arrays; -import org.joda.time.DateTime; - -public class TestHelper { - public static ItemData itemDataFixture(String seed, User user) { - DateTime now = new DateTime(); - return new ItemData( - seed + "id", - "title-" + seed, - "title " + seed, - "desc " + seed, - "image" + seed, - false, - 0, - now, - now, - new ArrayList<>(), - new ProfileData(user.getId(), user.getUsername(), user.getBio(), user.getImage(), false)); - } - - public static ItemData getItemDataFromItemAndUser(Item item, User user) { - return new ItemData( - item.getId(), - item.getSlug(), - item.getTitle(), - item.getDescription(), - item.getImage(), - false, - 0, - item.getCreatedAt(), - item.getUpdatedAt(), - Arrays.asList("joda"), - new ProfileData(user.getId(), user.getUsername(), user.getBio(), user.getImage(), false)); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/api/CommentsApiTest.java b/.framework/java/backend/src/test/java/io/spring/api/CommentsApiTest.java deleted file mode 100644 index c13246e..0000000 --- a/.framework/java/backend/src/test/java/io/spring/api/CommentsApiTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package io.spring.api; - -import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import io.restassured.module.mockmvc.RestAssuredMockMvc; -import io.spring.JacksonCustomizations; -import io.spring.api.security.WebSecurityConfig; -import io.spring.application.CommentQueryService; -import io.spring.application.data.CommentData; -import io.spring.application.data.ProfileData; -import io.spring.core.comment.Comment; -import io.spring.core.comment.CommentRepository; -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.test.web.servlet.MockMvc; - -@WebMvcTest(CommentsApi.class) -@Import({WebSecurityConfig.class, JacksonCustomizations.class}) -public class CommentsApiTest extends TestWithCurrentUser { - - @MockBean private ItemRepository itemRepository; - - @MockBean private CommentRepository commentRepository; - @MockBean private CommentQueryService commentQueryService; - - private Item item; - private CommentData commentData; - private Comment comment; - @Autowired private MockMvc mvc; - - @BeforeEach - public void setUp() throws Exception { - RestAssuredMockMvc.mockMvc(mvc); - super.setUp(); - item = new Item("title", "desc", "image", Arrays.asList("test", "java"), user.getId()); - when(itemRepository.findBySlug(eq(item.getSlug()))).thenReturn(Optional.of(item)); - comment = new Comment("comment", user.getId(), item.getId()); - commentData = - new CommentData( - comment.getId(), - comment.getBody(), - comment.getItemId(), - comment.getCreatedAt(), - comment.getCreatedAt(), - new ProfileData( - user.getId(), user.getUsername(), user.getBio(), user.getImage(), false)); - } - - @Test - public void should_create_comment_success() throws Exception { - Map param = - new HashMap() { - { - put( - "comment", - new HashMap() { - { - put("body", "comment content"); - } - }); - } - }; - - when(commentQueryService.findById(anyString(), eq(user))).thenReturn(Optional.of(commentData)); - - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(param) - .when() - .post("/api/items/{slug}/comments", item.getSlug()) - .then() - .statusCode(201) - .body("comment.body", equalTo(commentData.getBody())); - } - - @Test - public void should_get_422_with_empty_body() throws Exception { - Map param = - new HashMap() { - { - put( - "comment", - new HashMap() { - { - put("body", ""); - } - }); - } - }; - - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(param) - .when() - .post("/api/items/{slug}/comments", item.getSlug()) - .then() - .statusCode(422) - .body("errors.body[0]", equalTo("can't be empty")); - } - - @Test - public void should_get_comments_of_item_success() throws Exception { - when(commentQueryService.findByItemId(anyString(), eq(null))) - .thenReturn(Arrays.asList(commentData)); - RestAssuredMockMvc.when() - .get("/api/items/{slug}/comments", item.getSlug()) - .prettyPeek() - .then() - .statusCode(200) - .body("comments[0].id", equalTo(commentData.getId())); - } - - @Test - public void should_delete_comment_success() throws Exception { - when(commentRepository.findById(eq(item.getId()), eq(comment.getId()))) - .thenReturn(Optional.of(comment)); - - given() - .header("Authorization", "Token " + token) - .when() - .delete("/api/items/{slug}/comments/{id}", item.getSlug(), comment.getId()) - .then() - .statusCode(204); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/api/CurrentUserApiTest.java b/.framework/java/backend/src/test/java/io/spring/api/CurrentUserApiTest.java deleted file mode 100644 index e00e05c..0000000 --- a/.framework/java/backend/src/test/java/io/spring/api/CurrentUserApiTest.java +++ /dev/null @@ -1,179 +0,0 @@ -package io.spring.api; - -import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import io.restassured.module.mockmvc.RestAssuredMockMvc; -import io.spring.JacksonCustomizations; -import io.spring.api.security.WebSecurityConfig; -import io.spring.application.UserQueryService; -import io.spring.application.user.UserService; -import io.spring.core.user.User; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.test.web.servlet.MockMvc; - -@WebMvcTest(CurrentUserApi.class) -@Import({ - WebSecurityConfig.class, - JacksonCustomizations.class, - UserService.class, - ValidationAutoConfiguration.class, - BCryptPasswordEncoder.class -}) -public class CurrentUserApiTest extends TestWithCurrentUser { - - @Autowired private MockMvc mvc; - - @MockBean private UserQueryService userQueryService; - - @Override - @BeforeEach - public void setUp() throws Exception { - super.setUp(); - RestAssuredMockMvc.mockMvc(mvc); - } - - @Test - public void should_get_current_user_with_token() throws Exception { - when(userQueryService.findById(any())).thenReturn(Optional.of(userData)); - - given() - .header("Authorization", "Token " + token) - .contentType("application/json") - .when() - .get("/api/user") - .then() - .statusCode(200) - .body("user.email", equalTo(email)) - .body("user.username", equalTo(username)) - .body("user.bio", equalTo("")) - .body("user.image", equalTo(defaultAvatar)) - .body("user.token", equalTo(token)); - } - - @Test - public void should_get_401_without_token() throws Exception { - given().contentType("application/json").when().get("/api/user").then().statusCode(401); - } - - @Test - public void should_get_401_with_invalid_token() throws Exception { - String invalidToken = "asdfasd"; - when(jwtService.getSubFromToken(eq(invalidToken))).thenReturn(Optional.empty()); - given() - .contentType("application/json") - .header("Authorization", "Token " + invalidToken) - .when() - .get("/api/user") - .then() - .statusCode(401); - } - - @Test - public void should_update_current_user_profile() throws Exception { - String newEmail = "newemail@example.com"; - String newBio = "updated"; - String newUsername = "newusernamee"; - - Map param = - new HashMap() { - { - put( - "user", - new HashMap() { - { - put("email", newEmail); - put("bio", newBio); - put("username", newUsername); - } - }); - } - }; - - when(userRepository.findByUsername(eq(newUsername))).thenReturn(Optional.empty()); - when(userRepository.findByEmail(eq(newEmail))).thenReturn(Optional.empty()); - - when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData)); - - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(param) - .when() - .put("/api/user") - .then() - .statusCode(200); - } - - @Test - public void should_get_error_if_email_exists_when_update_user_profile() throws Exception { - String newEmail = "newemail@example.com"; - String newBio = "updated"; - String newUsername = "newusernamee"; - - Map param = prepareUpdateParam(newEmail, newBio, newUsername); - - when(userRepository.findByEmail(eq(newEmail))) - .thenReturn(Optional.of(new User(newEmail, "username", "123", "", ""))); - when(userRepository.findByUsername(eq(newUsername))).thenReturn(Optional.empty()); - - when(userQueryService.findById(eq(user.getId()))).thenReturn(Optional.of(userData)); - - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(param) - .when() - .put("/api/user") - .prettyPeek() - .then() - .statusCode(422) - .body("errors.email[0]", equalTo("email already exist")); - } - - private HashMap prepareUpdateParam( - final String newEmail, final String newBio, final String newUsername) { - return new HashMap() { - { - put( - "user", - new HashMap() { - { - put("email", newEmail); - put("bio", newBio); - put("username", newUsername); - } - }); - } - }; - } - - @Test - public void should_get_401_if_not_login() throws Exception { - given() - .contentType("application/json") - .body( - new HashMap() { - { - put("user", new HashMap()); - } - }) - .when() - .put("/api/user") - .then() - .statusCode(401); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/api/ItemApiTest.java b/.framework/java/backend/src/test/java/io/spring/api/ItemApiTest.java deleted file mode 100644 index c521582..0000000 --- a/.framework/java/backend/src/test/java/io/spring/api/ItemApiTest.java +++ /dev/null @@ -1,222 +0,0 @@ -package io.spring.api; - -import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import io.restassured.module.mockmvc.RestAssuredMockMvc; -import io.spring.JacksonCustomizations; -import io.spring.TestHelper; -import io.spring.api.security.WebSecurityConfig; -import io.spring.application.ItemQueryService; -import io.spring.application.data.ItemData; -import io.spring.application.data.ProfileData; -import io.spring.application.item.ItemCommandService; -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.user.User; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import org.joda.time.DateTime; -import org.joda.time.format.ISODateTimeFormat; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.test.web.servlet.MockMvc; - -@WebMvcTest({ItemApi.class}) -@Import({WebSecurityConfig.class, JacksonCustomizations.class}) -public class ItemApiTest extends TestWithCurrentUser { - @Autowired private MockMvc mvc; - - @MockBean private ItemQueryService itemQueryService; - - @MockBean private ItemRepository itemRepository; - - @MockBean ItemCommandService itemCommandService; - - @Override - @BeforeEach - public void setUp() throws Exception { - super.setUp(); - RestAssuredMockMvc.mockMvc(mvc); - } - - @Test - public void should_read_item_success() throws Exception { - String slug = "test-new-item"; - DateTime time = new DateTime(); - Item item = - new Item( - "Test New Item", - "Desc", - "Image", - Arrays.asList("java", "spring", "jpg"), - user.getId(), - time); - ItemData itemData = TestHelper.getItemDataFromItemAndUser(item, user); - - when(itemQueryService.findBySlug(eq(slug), eq(null))).thenReturn(Optional.of(itemData)); - - RestAssuredMockMvc.when() - .get("/api/items/{slug}", slug) - .then() - .statusCode(200) - .body("item.slug", equalTo(slug)) - .body("item.image", equalTo(itemData.getImage())) - .body("item.createdAt", equalTo(ISODateTimeFormat.dateTime().withZoneUTC().print(time))); - } - - @Test - public void should_404_if_item_not_found() throws Exception { - when(itemQueryService.findBySlug(anyString(), any())).thenReturn(Optional.empty()); - RestAssuredMockMvc.when().get("/api/items/not-exists").then().statusCode(404); - } - - @Test - public void should_update_item_content_success() throws Exception { - List tagList = Arrays.asList("java", "spring", "jpg"); - - Item originalItem = - new Item("old title", "old description", "old image", tagList, user.getId()); - - Item updatedItem = new Item("new title", "new description", "old image", tagList, user.getId()); - - Map updateParam = - prepareUpdateParam( - updatedItem.getTitle(), updatedItem.getImage(), updatedItem.getDescription()); - - ItemData updatedItemData = TestHelper.getItemDataFromItemAndUser(updatedItem, user); - - when(itemRepository.findBySlug(eq(originalItem.getSlug()))) - .thenReturn(Optional.of(originalItem)); - when(itemCommandService.updateItem(eq(originalItem), any())).thenReturn(updatedItem); - when(itemQueryService.findBySlug(eq(updatedItem.getSlug()), eq(user))) - .thenReturn(Optional.of(updatedItemData)); - - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(updateParam) - .when() - .put("/api/items/{slug}", originalItem.getSlug()) - .then() - .statusCode(200) - .body("item.slug", equalTo(updatedItemData.getSlug())); - } - - @Test - public void should_get_403_if_not_user_to_update_item() throws Exception { - String title = "new-title"; - String image = "new image"; - String description = "new description"; - Map updateParam = prepareUpdateParam(title, image, description); - - User anotherUser = new User("test@test.com", "test", "123123", "", ""); - - Item item = - new Item( - title, description, image, Arrays.asList("java", "spring", "jpg"), anotherUser.getId()); - - DateTime time = new DateTime(); - ItemData itemData = - new ItemData( - item.getId(), - item.getSlug(), - item.getTitle(), - item.getDescription(), - item.getImage(), - false, - 0, - time, - time, - Arrays.asList("joda"), - new ProfileData( - anotherUser.getId(), - anotherUser.getUsername(), - anotherUser.getBio(), - anotherUser.getImage(), - false)); - - when(itemRepository.findBySlug(eq(item.getSlug()))).thenReturn(Optional.of(item)); - when(itemQueryService.findBySlug(eq(item.getSlug()), eq(user))) - .thenReturn(Optional.of(itemData)); - - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(updateParam) - .when() - .put("/api/items/{slug}", item.getSlug()) - .then() - .statusCode(403); - } - - @Test - public void should_delete_item_success() throws Exception { - String title = "title"; - String image = "image"; - String description = "description"; - - Item item = - new Item(title, description, image, Arrays.asList("java", "spring", "jpg"), user.getId()); - when(itemRepository.findBySlug(eq(item.getSlug()))).thenReturn(Optional.of(item)); - - given() - .header("Authorization", "Token " + token) - .when() - .delete("/api/items/{slug}", item.getSlug()) - .then() - .statusCode(204); - - verify(itemRepository).remove(eq(item)); - } - - @Test - public void should_403_if_not_author_delete_item() throws Exception { - String title = "new-title"; - String image = "new image"; - String description = "new description"; - - User anotherUser = new User("test@test.com", "test", "123123", "", ""); - - Item item = - new Item( - title, description, image, Arrays.asList("java", "spring", "jpg"), anotherUser.getId()); - - when(itemRepository.findBySlug(eq(item.getSlug()))).thenReturn(Optional.of(item)); - given() - .header("Authorization", "Token " + token) - .when() - .delete("/api/items/{slug}", item.getSlug()) - .then() - .statusCode(403); - } - - private HashMap prepareUpdateParam( - final String title, final String image, final String description) { - return new HashMap() { - { - put( - "item", - new HashMap() { - { - put("title", title); - put("image", image); - put("description", description); - } - }); - } - }; - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/api/ItemFavoriteApiTest.java b/.framework/java/backend/src/test/java/io/spring/api/ItemFavoriteApiTest.java deleted file mode 100644 index 5455a3c..0000000 --- a/.framework/java/backend/src/test/java/io/spring/api/ItemFavoriteApiTest.java +++ /dev/null @@ -1,103 +0,0 @@ -package io.spring.api; - -import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import io.restassured.module.mockmvc.RestAssuredMockMvc; -import io.spring.JacksonCustomizations; -import io.spring.api.security.WebSecurityConfig; -import io.spring.application.ItemQueryService; -import io.spring.application.data.ItemData; -import io.spring.application.data.ProfileData; -import io.spring.core.favorite.ItemFavorite; -import io.spring.core.favorite.ItemFavoriteRepository; -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.item.Tag; -import io.spring.core.user.User; -import java.util.Arrays; -import java.util.Optional; -import java.util.stream.Collectors; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.test.web.servlet.MockMvc; - -@WebMvcTest(ItemFavoriteApi.class) -@Import({WebSecurityConfig.class, JacksonCustomizations.class}) -public class ItemFavoriteApiTest extends TestWithCurrentUser { - @Autowired private MockMvc mvc; - - @MockBean private ItemFavoriteRepository itemFavoriteRepository; - - @MockBean private ItemRepository itemRepository; - - @MockBean private ItemQueryService itemQueryService; - - private Item item; - - @BeforeEach - public void setUp() throws Exception { - super.setUp(); - RestAssuredMockMvc.mockMvc(mvc); - User anotherUser = new User("other@test.com", "other", "123", "", ""); - item = new Item("title", "desc", "image", Arrays.asList("java"), anotherUser.getId()); - when(itemRepository.findBySlug(eq(item.getSlug()))).thenReturn(Optional.of(item)); - ItemData itemData = - new ItemData( - item.getId(), - item.getSlug(), - item.getTitle(), - item.getDescription(), - item.getImage(), - true, - 1, - item.getCreatedAt(), - item.getUpdatedAt(), - item.getTags().stream().map(Tag::getName).collect(Collectors.toList()), - new ProfileData( - anotherUser.getId(), - anotherUser.getUsername(), - anotherUser.getBio(), - anotherUser.getImage(), - false)); - when(itemQueryService.findBySlug(eq(itemData.getSlug()), eq(user))) - .thenReturn(Optional.of(itemData)); - } - - @Test - public void should_favorite_an_item_success() throws Exception { - given() - .header("Authorization", "Token " + token) - .when() - .post("/api/items/{slug}/favorite", item.getSlug()) - .prettyPeek() - .then() - .statusCode(200) - .body("item.id", equalTo(item.getId())); - - verify(itemFavoriteRepository).save(any()); - } - - @Test - public void should_unfavorite_an_item_success() throws Exception { - when(itemFavoriteRepository.find(eq(item.getId()), eq(user.getId()))) - .thenReturn(Optional.of(new ItemFavorite(item.getId(), user.getId()))); - given() - .header("Authorization", "Token " + token) - .when() - .delete("/api/items/{slug}/favorite", item.getSlug()) - .prettyPeek() - .then() - .statusCode(200) - .body("item.id", equalTo(item.getId())); - verify(itemFavoriteRepository).remove(new ItemFavorite(item.getId(), user.getId())); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/api/ItemsApiTest.java b/.framework/java/backend/src/test/java/io/spring/api/ItemsApiTest.java deleted file mode 100644 index bc02966..0000000 --- a/.framework/java/backend/src/test/java/io/spring/api/ItemsApiTest.java +++ /dev/null @@ -1,174 +0,0 @@ -package io.spring.api; - -import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; -import static java.util.Arrays.asList; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import io.restassured.module.mockmvc.RestAssuredMockMvc; -import io.spring.JacksonCustomizations; -import io.spring.api.security.WebSecurityConfig; -import io.spring.application.ItemQueryService; -import io.spring.application.data.ItemData; -import io.spring.application.data.ProfileData; -import io.spring.application.item.ItemCommandService; -import io.spring.core.item.Item; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import org.joda.time.DateTime; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.test.web.servlet.MockMvc; - -@WebMvcTest({ItemsApi.class}) -@Import({WebSecurityConfig.class, JacksonCustomizations.class}) -public class ItemsApiTest extends TestWithCurrentUser { - @Autowired private MockMvc mvc; - - @MockBean private ItemQueryService itemQueryService; - - @MockBean private ItemCommandService itemCommandService; - - @Override - @BeforeEach - public void setUp() throws Exception { - super.setUp(); - RestAssuredMockMvc.mockMvc(mvc); - } - - @Test - public void should_create_item_success() throws Exception { - String title = "How to train your dragon"; - String slug = "how-to-train-your-dragon"; - String description = "Ever wonder how?"; - String image = "Another image"; - List tagList = asList("reactjs", "angularjs", "dragons"); - Map param = prepareParam(title, description, image, tagList); - - ItemData itemData = - new ItemData( - "123", - slug, - title, - description, - image, - false, - 0, - new DateTime(), - new DateTime(), - tagList, - new ProfileData("userid", user.getUsername(), user.getBio(), user.getImage(), false)); - - when(itemCommandService.createItem(any(), any())) - .thenReturn(new Item(title, description, image, tagList, user.getId())); - - when(itemQueryService.findBySlug(eq(Item.toSlug(title)), any())).thenReturn(Optional.empty()); - - when(itemQueryService.findById(any(), any())).thenReturn(Optional.of(itemData)); - - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(param) - .when() - .post("/api/items") - .then() - .statusCode(200) - .body("item.title", equalTo(title)) - .body("item.favorited", equalTo(false)) - .body("item.favoritesCount", equalTo(0)) - .body("item.seller.username", equalTo(user.getUsername())) - .body("item.seller.id", equalTo(null)); - - verify(itemCommandService).createItem(any(), any()); - } - - @Test - public void should_get_error_message_with_wrong_parameter() throws Exception { - String title = ""; - String description = "Ever wonder how?"; - String image = "Image URL"; - String[] tagList = {"reactjs", "angularjs", "dragons"}; - Map param = prepareParam(title, description, image, asList(tagList)); - - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(param) - .when() - .post("/api/items") - .prettyPeek() - .then() - .statusCode(422) - .body("errors.title[0]", equalTo("can't be empty")); - } - - @Test - public void should_get_error_message_with_duplicated_title() { - String title = "How to train your dragon"; - String slug = "how-to-train-your-dragon"; - String description = "Ever wonder how?"; - String image = "Image URL"; - String[] tagList = {"reactjs", "angularjs", "dragons"}; - Map param = prepareParam(title, description, image, asList(tagList)); - - ItemData itemData = - new ItemData( - "123", - slug, - title, - description, - image, - false, - 0, - new DateTime(), - new DateTime(), - asList(tagList), - new ProfileData("userid", user.getUsername(), user.getBio(), user.getImage(), false)); - - when(itemQueryService.findBySlug(eq(Item.toSlug(title)), any())) - .thenReturn(Optional.of(itemData)); - - when(itemQueryService.findById(any(), any())).thenReturn(Optional.of(itemData)); - - given() - .contentType("application/json") - .header("Authorization", "Token " + token) - .body(param) - .when() - .post("/api/items") - .prettyPeek() - .then() - .statusCode(422); - } - - private HashMap prepareParam( - final String title, - final String description, - final String image, - final List tagList) { - return new HashMap() { - { - put( - "item", - new HashMap() { - { - put("title", title); - put("description", description); - put("image", image); - put("tagList", tagList); - } - }); - } - }; - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/api/ListItemApiTest.java b/.framework/java/backend/src/test/java/io/spring/api/ListItemApiTest.java deleted file mode 100644 index 9f36d2c..0000000 --- a/.framework/java/backend/src/test/java/io/spring/api/ListItemApiTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package io.spring.api; - -import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; -import static io.spring.TestHelper.itemDataFixture; -import static java.util.Arrays.asList; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import io.restassured.module.mockmvc.RestAssuredMockMvc; -import io.spring.JacksonCustomizations; -import io.spring.api.security.WebSecurityConfig; -import io.spring.application.ItemQueryService; -import io.spring.application.Page; -import io.spring.application.data.ItemDataList; -import io.spring.application.item.ItemCommandService; -import io.spring.core.item.ItemRepository; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.test.web.servlet.MockMvc; - -@WebMvcTest(ItemsApi.class) -@Import({WebSecurityConfig.class, JacksonCustomizations.class}) -public class ListItemApiTest extends TestWithCurrentUser { - @MockBean private ItemRepository itemRepository; - - @MockBean private ItemQueryService itemQueryService; - - @MockBean private ItemCommandService itemCommandService; - - @Autowired private MockMvc mvc; - - @Override - @BeforeEach - public void setUp() throws Exception { - super.setUp(); - RestAssuredMockMvc.mockMvc(mvc); - } - - @Test - public void should_get_default_item_list() throws Exception { - ItemDataList itemDataList = - new ItemDataList(asList(itemDataFixture("1", user), itemDataFixture("2", user)), 2); - when(itemQueryService.findRecentItems( - eq(null), eq(null), eq(null), eq(new Page(0, 20)), eq(null))) - .thenReturn(itemDataList); - RestAssuredMockMvc.when().get("/api/items").prettyPeek().then().statusCode(200); - } - - @Test - public void should_get_feeds_401_without_login() throws Exception { - RestAssuredMockMvc.when().get("/api/items/feed").prettyPeek().then().statusCode(401); - } - - @Test - public void should_get_feeds_success() throws Exception { - ItemDataList itemDataList = - new ItemDataList(asList(itemDataFixture("1", user), itemDataFixture("2", user)), 2); - when(itemQueryService.findUserFeed(eq(user), eq(new Page(0, 20)))).thenReturn(itemDataList); - - given() - .header("Authorization", "Token " + token) - .when() - .get("/api/items/feed") - .prettyPeek() - .then() - .statusCode(200); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/api/ProfileApiTest.java b/.framework/java/backend/src/test/java/io/spring/api/ProfileApiTest.java deleted file mode 100644 index 2e3ab9c..0000000 --- a/.framework/java/backend/src/test/java/io/spring/api/ProfileApiTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.spring.api; - -import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import io.restassured.module.mockmvc.RestAssuredMockMvc; -import io.spring.JacksonCustomizations; -import io.spring.api.security.WebSecurityConfig; -import io.spring.application.ProfileQueryService; -import io.spring.application.data.ProfileData; -import io.spring.core.user.FollowRelation; -import io.spring.core.user.User; -import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.test.web.servlet.MockMvc; - -@WebMvcTest(ProfileApi.class) -@Import({WebSecurityConfig.class, JacksonCustomizations.class}) -public class ProfileApiTest extends TestWithCurrentUser { - private User anotherUser; - - @Autowired private MockMvc mvc; - - @MockBean private ProfileQueryService profileQueryService; - - private ProfileData profileData; - - @BeforeEach - public void setUp() throws Exception { - super.setUp(); - RestAssuredMockMvc.mockMvc(mvc); - anotherUser = new User("username@test.com", "username", "123", "", ""); - profileData = - new ProfileData( - anotherUser.getId(), - anotherUser.getUsername(), - anotherUser.getBio(), - anotherUser.getImage(), - false); - when(userRepository.findByUsername(eq(anotherUser.getUsername()))) - .thenReturn(Optional.of(anotherUser)); - } - - @Test - public void should_get_user_profile_success() throws Exception { - when(profileQueryService.findByUsername(eq(profileData.getUsername()), eq(null))) - .thenReturn(Optional.of(profileData)); - RestAssuredMockMvc.when() - .get("/api/profiles/{username}", profileData.getUsername()) - .prettyPeek() - .then() - .statusCode(200) - .body("profile.username", equalTo(profileData.getUsername())); - } - - @Test - public void should_follow_user_success() throws Exception { - when(profileQueryService.findByUsername(eq(profileData.getUsername()), eq(user))) - .thenReturn(Optional.of(profileData)); - given() - .header("Authorization", "Token " + token) - .when() - .post("/api/profiles/{username}/follow", anotherUser.getUsername()) - .prettyPeek() - .then() - .statusCode(200); - verify(userRepository).saveRelation(new FollowRelation(user.getId(), anotherUser.getId())); - } - - @Test - public void should_unfollow_user_success() throws Exception { - FollowRelation followRelation = new FollowRelation(user.getId(), anotherUser.getId()); - when(userRepository.findRelation(eq(user.getId()), eq(anotherUser.getId()))) - .thenReturn(Optional.of(followRelation)); - when(profileQueryService.findByUsername(eq(profileData.getUsername()), eq(user))) - .thenReturn(Optional.of(profileData)); - - given() - .header("Authorization", "Token " + token) - .when() - .delete("/api/profiles/{username}/follow", anotherUser.getUsername()) - .prettyPeek() - .then() - .statusCode(200); - - verify(userRepository).removeRelation(eq(followRelation)); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/api/TestWithCurrentUser.java b/.framework/java/backend/src/test/java/io/spring/api/TestWithCurrentUser.java deleted file mode 100644 index 7d3b104..0000000 --- a/.framework/java/backend/src/test/java/io/spring/api/TestWithCurrentUser.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.spring.api; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import io.spring.application.data.UserData; -import io.spring.core.service.JwtService; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.mybatis.readservice.UserReadService; -import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; -import org.springframework.boot.test.mock.mockito.MockBean; - -abstract class TestWithCurrentUser { - @MockBean protected UserRepository userRepository; - - @MockBean protected UserReadService userReadService; - - protected User user; - protected UserData userData; - protected String token; - protected String email; - protected String username; - protected String defaultAvatar; - - @MockBean protected JwtService jwtService; - - protected void userFixture() { - email = "john@jacob.com"; - username = "johnjacob"; - defaultAvatar = "https://static.productionready.io/images/smiley-cyrus.jpg"; - - user = new User(email, username, "123", "", defaultAvatar); - when(userRepository.findByUsername(eq(username))).thenReturn(Optional.of(user)); - when(userRepository.findById(eq(user.getId()))).thenReturn(Optional.of(user)); - - userData = new UserData(user.getId(), email, username, "", defaultAvatar); - when(userReadService.findById(eq(user.getId()))).thenReturn(userData); - - token = "token"; - when(jwtService.getSubFromToken(eq(token))).thenReturn(Optional.of(user.getId())); - } - - @BeforeEach - public void setUp() throws Exception { - userFixture(); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/api/UsersApiTest.java b/.framework/java/backend/src/test/java/io/spring/api/UsersApiTest.java deleted file mode 100644 index 2eb3ba6..0000000 --- a/.framework/java/backend/src/test/java/io/spring/api/UsersApiTest.java +++ /dev/null @@ -1,271 +0,0 @@ -package io.spring.api; - -import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import io.restassured.module.mockmvc.RestAssuredMockMvc; -import io.spring.JacksonCustomizations; -import io.spring.api.security.WebSecurityConfig; -import io.spring.application.UserQueryService; -import io.spring.application.data.UserData; -import io.spring.application.user.UserService; -import io.spring.core.service.JwtService; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.mybatis.readservice.UserReadService; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.test.web.servlet.MockMvc; - -@WebMvcTest(UsersApi.class) -@Import({ - WebSecurityConfig.class, - UserQueryService.class, - BCryptPasswordEncoder.class, - JacksonCustomizations.class -}) -public class UsersApiTest { - @Autowired private MockMvc mvc; - - @MockBean private UserRepository userRepository; - - @MockBean private JwtService jwtService; - - @MockBean private UserReadService userReadService; - - @MockBean private UserService userService; - - @Autowired private PasswordEncoder passwordEncoder; - - private String defaultAvatar; - - @BeforeEach - public void setUp() throws Exception { - RestAssuredMockMvc.mockMvc(mvc); - defaultAvatar = "https://static.productionready.io/images/smiley-cyrus.jpg"; - } - - @Test - public void should_create_user_success() throws Exception { - String email = "john@jacob.com"; - String username = "johnjacob"; - - when(jwtService.toToken(any())).thenReturn("123"); - User user = new User(email, username, "123", "", defaultAvatar); - UserData userData = new UserData(user.getId(), email, username, "", defaultAvatar); - when(userReadService.findById(any())).thenReturn(userData); - - when(userService.createUser(any())).thenReturn(user); - - when(userRepository.findByUsername(eq(username))).thenReturn(Optional.empty()); - when(userRepository.findByEmail(eq(email))).thenReturn(Optional.empty()); - - Map param = prepareRegisterParameter(email, username); - - given() - .contentType("application/json") - .body(param) - .when() - .post("/api/users") - .then() - .statusCode(201) - .body("user.email", equalTo(email)) - .body("user.username", equalTo(username)) - .body("user.bio", equalTo("")) - .body("user.image", equalTo(defaultAvatar)) - .body("user.token", equalTo("123")); - - verify(userService).createUser(any()); - } - - @Test - public void should_show_error_message_for_blank_username() throws Exception { - - String email = "john@jacob.com"; - String username = ""; - - Map param = prepareRegisterParameter(email, username); - - given() - .contentType("application/json") - .body(param) - .when() - .post("/api/users") - .prettyPeek() - .then() - .statusCode(422) - .body("errors.username[0]", equalTo("can't be empty")); - } - - @Test - public void should_show_error_message_for_invalid_email() throws Exception { - String email = "johnxjacob.com"; - String username = "johnjacob"; - - Map param = prepareRegisterParameter(email, username); - - given() - .contentType("application/json") - .body(param) - .when() - .post("/api/users") - .prettyPeek() - .then() - .statusCode(422) - .body("errors.email[0]", equalTo("should be an email")); - } - - @Test - public void should_show_error_for_duplicated_username() throws Exception { - String email = "john@jacob.com"; - String username = "johnjacob"; - - when(userRepository.findByUsername(eq(username))) - .thenReturn(Optional.of(new User(email, username, "123", "bio", ""))); - when(userRepository.findByEmail(any())).thenReturn(Optional.empty()); - - Map param = prepareRegisterParameter(email, username); - - given() - .contentType("application/json") - .body(param) - .when() - .post("/api/users") - .prettyPeek() - .then() - .statusCode(422) - .body("errors.username[0]", equalTo("duplicated username")); - } - - @Test - public void should_show_error_for_duplicated_email() throws Exception { - String email = "john@jacob.com"; - String username = "johnjacob2"; - - when(userRepository.findByEmail(eq(email))) - .thenReturn(Optional.of(new User(email, username, "123", "bio", ""))); - - when(userRepository.findByUsername(eq(username))).thenReturn(Optional.empty()); - - Map param = prepareRegisterParameter(email, username); - - given() - .contentType("application/json") - .body(param) - .when() - .post("/api/users") - .then() - .statusCode(422) - .body("errors.email[0]", equalTo("duplicated email")); - } - - private HashMap prepareRegisterParameter( - final String email, final String username) { - return new HashMap() { - { - put( - "user", - new HashMap() { - { - put("email", email); - put("password", "johnnyjacob"); - put("username", username); - } - }); - } - }; - } - - @Test - public void should_login_success() throws Exception { - String email = "john@jacob.com"; - String username = "johnjacob2"; - String password = "123"; - - User user = new User(email, username, passwordEncoder.encode(password), "", defaultAvatar); - UserData userData = new UserData("123", email, username, "", defaultAvatar); - - when(userRepository.findByEmail(eq(email))).thenReturn(Optional.of(user)); - when(userReadService.findByUsername(eq(username))).thenReturn(userData); - when(userReadService.findById(eq(user.getId()))).thenReturn(userData); - when(jwtService.toToken(any())).thenReturn("123"); - - Map param = - new HashMap() { - { - put( - "user", - new HashMap() { - { - put("email", email); - put("password", password); - } - }); - } - }; - - given() - .contentType("application/json") - .body(param) - .when() - .post("/api/users/login") - .then() - .statusCode(200) - .body("user.email", equalTo(email)) - .body("user.username", equalTo(username)) - .body("user.bio", equalTo("")) - .body("user.image", equalTo(defaultAvatar)) - .body("user.token", equalTo("123")); - ; - } - - @Test - public void should_fail_login_with_wrong_password() throws Exception { - String email = "john@jacob.com"; - String username = "johnjacob2"; - String password = "123"; - - User user = new User(email, username, password, "", defaultAvatar); - UserData userData = new UserData(user.getId(), email, username, "", defaultAvatar); - - when(userRepository.findByEmail(eq(email))).thenReturn(Optional.of(user)); - when(userReadService.findByUsername(eq(username))).thenReturn(userData); - - Map param = - new HashMap() { - { - put( - "user", - new HashMap() { - { - put("email", email); - put("password", "123123"); - } - }); - } - }; - - given() - .contentType("application/json") - .body(param) - .when() - .post("/api/users/login") - .prettyPeek() - .then() - .statusCode(422) - .body("message", equalTo("invalid email or password")); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java b/.framework/java/backend/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java deleted file mode 100644 index e36bc35..0000000 --- a/.framework/java/backend/src/test/java/io/spring/application/comment/CommentQueryServiceTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.spring.application.comment; - -import io.spring.application.CommentQueryService; -import io.spring.application.data.CommentData; -import io.spring.core.comment.Comment; -import io.spring.core.comment.CommentRepository; -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.user.FollowRelation; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.DbTestBase; -import io.spring.infrastructure.repository.MyBatisCommentRepository; -import io.spring.infrastructure.repository.MyBatisItemRepository; -import io.spring.infrastructure.repository.MyBatisUserRepository; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; - -@Import({ - MyBatisCommentRepository.class, - MyBatisUserRepository.class, - CommentQueryService.class, - MyBatisItemRepository.class -}) -public class CommentQueryServiceTest extends DbTestBase { - @Autowired private CommentRepository commentRepository; - - @Autowired private UserRepository userRepository; - - @Autowired private CommentQueryService commentQueryService; - - @Autowired private ItemRepository itemRepository; - - private User user; - - @BeforeEach - public void setUp() { - user = new User("aisensiy@test.com", "aisensiy", "123", "", ""); - userRepository.save(user); - } - - @Test - public void should_read_comment_success() { - Comment comment = new Comment("content", user.getId(), "123"); - commentRepository.save(comment); - - Optional optional = commentQueryService.findById(comment.getId(), user); - Assertions.assertTrue(optional.isPresent()); - CommentData commentData = optional.get(); - Assertions.assertEquals(commentData.getProfileData().getUsername(), user.getUsername()); - } - - @Test - public void should_read_comments_of_item() { - Item item = new Item("title", "desc", "image", Arrays.asList("java"), user.getId()); - itemRepository.save(item); - - User user2 = new User("user2@email.com", "user2", "123", "", ""); - userRepository.save(user2); - userRepository.saveRelation(new FollowRelation(user.getId(), user2.getId())); - - Comment comment1 = new Comment("content1", user.getId(), item.getId()); - commentRepository.save(comment1); - Comment comment2 = new Comment("content2", user2.getId(), item.getId()); - commentRepository.save(comment2); - - List comments = commentQueryService.findByItemId(item.getId(), user); - Assertions.assertEquals(comments.size(), 2); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/application/item/ItemQueryServiceTest.java b/.framework/java/backend/src/test/java/io/spring/application/item/ItemQueryServiceTest.java deleted file mode 100644 index 7338ea6..0000000 --- a/.framework/java/backend/src/test/java/io/spring/application/item/ItemQueryServiceTest.java +++ /dev/null @@ -1,226 +0,0 @@ -package io.spring.application.item; - -import io.spring.application.CursorPageParameter; -import io.spring.application.CursorPager; -import io.spring.application.CursorPager.Direction; -import io.spring.application.DateTimeCursor; -import io.spring.application.ItemQueryService; -import io.spring.application.Page; -import io.spring.application.data.ItemData; -import io.spring.application.data.ItemDataList; -import io.spring.core.favorite.ItemFavorite; -import io.spring.core.favorite.ItemFavoriteRepository; -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.user.FollowRelation; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.DbTestBase; -import io.spring.infrastructure.repository.MyBatisItemFavoriteRepository; -import io.spring.infrastructure.repository.MyBatisItemRepository; -import io.spring.infrastructure.repository.MyBatisUserRepository; -import java.util.Arrays; -import java.util.Optional; -import org.joda.time.DateTime; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; - -@Import({ - ItemQueryService.class, - MyBatisUserRepository.class, - MyBatisItemRepository.class, - MyBatisItemFavoriteRepository.class -}) -public class ItemQueryServiceTest extends DbTestBase { - @Autowired private ItemQueryService queryService; - - @Autowired private ItemRepository itemRepository; - - @Autowired private UserRepository userRepository; - - @Autowired private ItemFavoriteRepository itemFavoriteRepository; - - private User user; - private Item item; - - @BeforeEach - public void setUp() { - user = new User("aisensiy@gmail.com", "aisensiy", "123", "", ""); - userRepository.save(user); - item = - new Item( - "test", "desc", "image", Arrays.asList("java", "spring"), user.getId(), new DateTime()); - itemRepository.save(item); - } - - @Test - public void should_fetch_item_success() { - Optional optional = queryService.findById(item.getId(), user); - Assertions.assertTrue(optional.isPresent()); - - ItemData fetched = optional.get(); - Assertions.assertEquals(fetched.getFavoritesCount(), 0); - Assertions.assertFalse(fetched.isFavorited()); - Assertions.assertNotNull(fetched.getCreatedAt()); - Assertions.assertNotNull(fetched.getUpdatedAt()); - Assertions.assertTrue(fetched.getTagList().contains("java")); - } - - @Test - public void should_get_item_with_right_favorite_and_favorite_count() { - User anotherUser = new User("other@test.com", "other", "123", "", ""); - userRepository.save(anotherUser); - itemFavoriteRepository.save(new ItemFavorite(item.getId(), anotherUser.getId())); - - Optional optional = queryService.findById(item.getId(), anotherUser); - Assertions.assertTrue(optional.isPresent()); - - ItemData itemData = optional.get(); - Assertions.assertEquals(itemData.getFavoritesCount(), 1); - Assertions.assertTrue(itemData.isFavorited()); - } - - @Test - public void should_get_default_item_list() { - Item anotherItem = - new Item( - "new item", - "desc", - "image", - Arrays.asList("test"), - user.getId(), - new DateTime().minusHours(1)); - itemRepository.save(anotherItem); - - ItemDataList recentItems = queryService.findRecentItems(null, null, null, new Page(), user); - Assertions.assertEquals(recentItems.getCount(), 2); - Assertions.assertEquals(recentItems.getItemDatas().size(), 2); - Assertions.assertEquals(recentItems.getItemDatas().get(0).getId(), item.getId()); - - ItemDataList nodata = queryService.findRecentItems(null, null, null, new Page(2, 10), user); - Assertions.assertEquals(nodata.getCount(), 2); - Assertions.assertEquals(nodata.getItemDatas().size(), 0); - } - - @Test - public void should_get_default_item_list_by_cursor() { - Item anotherItem = - new Item( - "new item", - "desc", - "image", - Arrays.asList("test"), - user.getId(), - new DateTime().minusHours(1)); - itemRepository.save(anotherItem); - - CursorPager recentItems = - queryService.findRecentItemsWithCursor( - null, null, null, new CursorPageParameter<>(null, 20, Direction.NEXT), user); - Assertions.assertEquals(recentItems.getData().size(), 2); - Assertions.assertEquals(recentItems.getData().get(0).getId(), item.getId()); - - CursorPager nodata = - queryService.findRecentItemsWithCursor( - null, - null, - null, - new CursorPageParameter( - DateTimeCursor.parse(recentItems.getEndCursor().toString()), 20, Direction.NEXT), - user); - Assertions.assertEquals(nodata.getData().size(), 0); - Assertions.assertEquals(nodata.getStartCursor(), null); - - CursorPager prevItems = - queryService.findRecentItemsWithCursor( - null, null, null, new CursorPageParameter<>(null, 20, Direction.PREV), user); - Assertions.assertEquals(prevItems.getData().size(), 2); - } - - @Test - public void should_query_item_by_seller() { - User anotherUser = new User("other@email.com", "other", "123", "", ""); - userRepository.save(anotherUser); - - Item anotherItem = - new Item("new item", "desc", "image", Arrays.asList("test"), anotherUser.getId()); - itemRepository.save(anotherItem); - - ItemDataList recentItems = - queryService.findRecentItems(null, user.getUsername(), null, new Page(), user); - Assertions.assertEquals(recentItems.getItemDatas().size(), 1); - Assertions.assertEquals(recentItems.getCount(), 1); - } - - @Test - public void should_query_item_by_favorite() { - User anotherUser = new User("other@email.com", "other", "123", "", ""); - userRepository.save(anotherUser); - - Item anotherItem = - new Item("new item", "desc", "image", Arrays.asList("test"), anotherUser.getId()); - itemRepository.save(anotherItem); - - ItemFavorite itemFavorite = new ItemFavorite(item.getId(), anotherUser.getId()); - itemFavoriteRepository.save(itemFavorite); - - ItemDataList recentItems = - queryService.findRecentItems( - null, null, anotherUser.getUsername(), new Page(), anotherUser); - Assertions.assertEquals(recentItems.getItemDatas().size(), 1); - Assertions.assertEquals(recentItems.getCount(), 1); - ItemData itemData = recentItems.getItemDatas().get(0); - Assertions.assertEquals(itemData.getId(), item.getId()); - Assertions.assertEquals(itemData.getFavoritesCount(), 1); - Assertions.assertTrue(itemData.isFavorited()); - } - - @Test - public void should_query_item_by_tag() { - Item anotherItem = new Item("new item", "desc", "image", Arrays.asList("test"), user.getId()); - itemRepository.save(anotherItem); - - ItemDataList recentItems = queryService.findRecentItems("spring", null, null, new Page(), user); - Assertions.assertEquals(recentItems.getItemDatas().size(), 1); - Assertions.assertEquals(recentItems.getCount(), 1); - Assertions.assertEquals(recentItems.getItemDatas().get(0).getId(), item.getId()); - - ItemDataList notag = queryService.findRecentItems("notag", null, null, new Page(), user); - Assertions.assertEquals(notag.getCount(), 0); - } - - @Test - public void should_show_following_if_user_followed_seller() { - User anotherUser = new User("other@email.com", "other", "123", "", ""); - userRepository.save(anotherUser); - - FollowRelation followRelation = new FollowRelation(anotherUser.getId(), user.getId()); - userRepository.saveRelation(followRelation); - - ItemDataList recentItems = - queryService.findRecentItems(null, null, null, new Page(), anotherUser); - Assertions.assertEquals(recentItems.getCount(), 1); - ItemData itemData = recentItems.getItemDatas().get(0); - Assertions.assertTrue(itemData.getProfileData().isFollowing()); - } - - @Test - public void should_get_user_feed() { - User anotherUser = new User("other@email.com", "other", "123", "", ""); - userRepository.save(anotherUser); - - FollowRelation followRelation = new FollowRelation(anotherUser.getId(), user.getId()); - userRepository.saveRelation(followRelation); - - ItemDataList userFeed = queryService.findUserFeed(user, new Page()); - Assertions.assertEquals(userFeed.getCount(), 0); - - ItemDataList anotherUserFeed = queryService.findUserFeed(anotherUser, new Page()); - Assertions.assertEquals(anotherUserFeed.getCount(), 1); - ItemData itemData = anotherUserFeed.getItemDatas().get(0); - Assertions.assertTrue(itemData.getProfileData().isFollowing()); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/application/profile/ProfileQueryServiceTest.java b/.framework/java/backend/src/test/java/io/spring/application/profile/ProfileQueryServiceTest.java deleted file mode 100644 index 34ce502..0000000 --- a/.framework/java/backend/src/test/java/io/spring/application/profile/ProfileQueryServiceTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.spring.application.profile; - -import io.spring.application.ProfileQueryService; -import io.spring.application.data.ProfileData; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.DbTestBase; -import io.spring.infrastructure.repository.MyBatisUserRepository; -import java.util.Optional; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; - -@Import({ProfileQueryService.class, MyBatisUserRepository.class}) -public class ProfileQueryServiceTest extends DbTestBase { - @Autowired private ProfileQueryService profileQueryService; - @Autowired private UserRepository userRepository; - - @Test - public void should_fetch_profile_success() { - User currentUser = new User("a@test.com", "a", "123", "", ""); - User profileUser = new User("p@test.com", "p", "123", "", ""); - userRepository.save(profileUser); - - Optional optional = - profileQueryService.findByUsername(profileUser.getUsername(), currentUser); - Assertions.assertTrue(optional.isPresent()); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java b/.framework/java/backend/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java deleted file mode 100644 index 084cae8..0000000 --- a/.framework/java/backend/src/test/java/io/spring/application/tag/TagsQueryServiceTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.spring.application.tag; - -import io.spring.application.TagsQueryService; -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.infrastructure.DbTestBase; -import io.spring.infrastructure.repository.MyBatisItemRepository; -import java.util.Arrays; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; - -@Import({TagsQueryService.class, MyBatisItemRepository.class}) -public class TagsQueryServiceTest extends DbTestBase { - @Autowired private TagsQueryService tagsQueryService; - - @Autowired private ItemRepository itemRepository; - - @Test - public void should_get_all_tags() { - itemRepository.save(new Item("test", "test", "image", Arrays.asList("java"), "123")); - Assertions.assertTrue(tagsQueryService.allTags().contains("java")); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/core/item/ItemTest.java b/.framework/java/backend/src/test/java/io/spring/core/item/ItemTest.java deleted file mode 100644 index e7cdd5c..0000000 --- a/.framework/java/backend/src/test/java/io/spring/core/item/ItemTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.spring.core.item; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.util.Arrays; -import org.junit.jupiter.api.Test; - -public class ItemTest { - - @Test - public void should_get_right_slug() { - Item item = new Item("a new title", "desc", "image", Arrays.asList("java"), "123"); - assertThat(item.getSlug(), is("a-new-title")); - } - - @Test - public void should_get_right_slug_with_number_in_title() { - Item item = new Item("a new title 2", "desc", "image", Arrays.asList("java"), "123"); - assertThat(item.getSlug(), is("a-new-title-2")); - } - - @Test - public void should_get_lower_case_slug() { - Item item = new Item("A NEW TITLE", "desc", "image", Arrays.asList("java"), "123"); - assertThat(item.getSlug(), is("a-new-title")); - } - - @Test - public void should_handle_other_language() { - Item item = new Item("中文:标题", "desc", "image", Arrays.asList("java"), "123"); - assertThat(item.getSlug(), is("中文-标题")); - } - - @Test - public void should_handle_commas() { - Item item = new Item("what?the.hell,w", "desc", "image", Arrays.asList("java"), "123"); - assertThat(item.getSlug(), is("what-the-hell-w")); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/infrastructure/DbTestBase.java b/.framework/java/backend/src/test/java/io/spring/infrastructure/DbTestBase.java deleted file mode 100644 index 80ed81c..0000000 --- a/.framework/java/backend/src/test/java/io/spring/infrastructure/DbTestBase.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.spring.infrastructure; - -import org.mybatis.spring.boot.test.autoconfigure.MybatisTest; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; -import org.springframework.test.context.ActiveProfiles; - -@ActiveProfiles("test") -@AutoConfigureTestDatabase(replace = Replace.NONE) -@MybatisTest -public abstract class DbTestBase {} diff --git a/.framework/java/backend/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java b/.framework/java/backend/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java deleted file mode 100644 index 8cc9a66..0000000 --- a/.framework/java/backend/src/test/java/io/spring/infrastructure/comment/MyBatisCommentRepositoryTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.spring.infrastructure.comment; - -import io.spring.core.comment.Comment; -import io.spring.core.comment.CommentRepository; -import io.spring.infrastructure.DbTestBase; -import io.spring.infrastructure.repository.MyBatisCommentRepository; -import java.util.Optional; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; - -@Import({MyBatisCommentRepository.class}) -public class MyBatisCommentRepositoryTest extends DbTestBase { - @Autowired private CommentRepository commentRepository; - - @Test - public void should_create_and_fetch_comment_success() { - Comment comment = new Comment("content", "123", "456"); - commentRepository.save(comment); - - Optional optional = commentRepository.findById("456", comment.getId()); - Assertions.assertTrue(optional.isPresent()); - Assertions.assertEquals(optional.get(), comment); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/infrastructure/favorite/MyBatisItemFavoriteRepositoryTest.java b/.framework/java/backend/src/test/java/io/spring/infrastructure/favorite/MyBatisItemFavoriteRepositoryTest.java deleted file mode 100644 index d94e73f..0000000 --- a/.framework/java/backend/src/test/java/io/spring/infrastructure/favorite/MyBatisItemFavoriteRepositoryTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.spring.infrastructure.favorite; - -import io.spring.core.favorite.ItemFavorite; -import io.spring.core.favorite.ItemFavoriteRepository; -import io.spring.infrastructure.DbTestBase; -import io.spring.infrastructure.repository.MyBatisItemFavoriteRepository; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; - -@Import({MyBatisItemFavoriteRepository.class}) -public class MyBatisItemFavoriteRepositoryTest extends DbTestBase { - @Autowired private ItemFavoriteRepository itemFavoriteRepository; - - @Autowired private io.spring.infrastructure.mybatis.mapper.ItemFavoriteMapper itemFavoriteMapper; - - @Test - public void should_save_and_fetch_itemFavorite_success() { - ItemFavorite itemFavorite = new ItemFavorite("123", "456"); - itemFavoriteRepository.save(itemFavorite); - Assertions.assertNotNull( - itemFavoriteMapper.find(itemFavorite.getItemId(), itemFavorite.getUserId())); - } - - @Test - public void should_remove_favorite_success() { - ItemFavorite itemFavorite = new ItemFavorite("123", "456"); - itemFavoriteRepository.save(itemFavorite); - itemFavoriteRepository.remove(itemFavorite); - Assertions.assertFalse(itemFavoriteRepository.find("123", "456").isPresent()); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/infrastructure/item/ItemRepositoryTransactionTest.java b/.framework/java/backend/src/test/java/io/spring/infrastructure/item/ItemRepositoryTransactionTest.java deleted file mode 100644 index 63c441e..0000000 --- a/.framework/java/backend/src/test/java/io/spring/infrastructure/item/ItemRepositoryTransactionTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.spring.infrastructure.item; - -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.mybatis.mapper.ItemMapper; -import java.util.Arrays; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; - -@ActiveProfiles("test") -@SpringBootTest -@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) -public class ItemRepositoryTransactionTest { - @Autowired private ItemRepository itemRepository; - - @Autowired private UserRepository userRepository; - - @Autowired private ItemMapper itemMapper; - - @Test - public void transactional_test() { - User user = new User("aisensiy@gmail.com", "aisensiy", "123", "bio", "default"); - userRepository.save(user); - Item item = new Item("test", "desc", "image", Arrays.asList("java", "spring"), user.getId()); - itemRepository.save(item); - Item anotherItem = - new Item("test", "desc", "image", Arrays.asList("java", "spring", "other"), user.getId()); - try { - itemRepository.save(anotherItem); - } catch (Exception e) { - Assertions.assertNull(itemMapper.findTag("other")); - } - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/infrastructure/item/MyBatisItemRepositoryTest.java b/.framework/java/backend/src/test/java/io/spring/infrastructure/item/MyBatisItemRepositoryTest.java deleted file mode 100644 index 8bdc808..0000000 --- a/.framework/java/backend/src/test/java/io/spring/infrastructure/item/MyBatisItemRepositoryTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package io.spring.infrastructure.item; - -import io.spring.core.item.Item; -import io.spring.core.item.ItemRepository; -import io.spring.core.item.Tag; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.DbTestBase; -import io.spring.infrastructure.repository.MyBatisItemRepository; -import io.spring.infrastructure.repository.MyBatisUserRepository; -import java.util.Arrays; -import java.util.Optional; -import java.util.Random; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; - -@Import({MyBatisItemRepository.class, MyBatisUserRepository.class}) -public class MyBatisItemRepositoryTest extends DbTestBase { - @Autowired private ItemRepository itemRepository; - - @Autowired private UserRepository userRepository; - - private Item item; - - @BeforeEach - public void setUp() { - Random random = new Random(); - int randomNumber = random.nextInt(); - User user = - new User( - "aisensiy" + randomNumber + "@gmail.com", "aisensiy" + randomNumber, "123", "", ""); - userRepository.save(user); - item = - new Item( - "test-" + randomNumber, "desc", "image", Arrays.asList("java", "spring"), user.getId()); - } - - @Test - public void should_create_and_fetch_item_success() { - itemRepository.save(item); - Optional optional = itemRepository.findById(item.getId()); - Assertions.assertTrue(optional.isPresent()); - Assertions.assertEquals(optional.get(), item); - Assertions.assertTrue(optional.get().getTags().contains(new Tag("java"))); - Assertions.assertTrue(optional.get().getTags().contains(new Tag("spring"))); - } - - @Test - public void should_update_and_fetch_item_success() { - itemRepository.save(item); - - String newTitle = "new test 2"; - item.update(newTitle, "", ""); - itemRepository.save(item); - System.out.println(item.getSlug()); - Optional optional = itemRepository.findBySlug(item.getSlug()); - Assertions.assertTrue(optional.isPresent()); - Item fetched = optional.get(); - Assertions.assertEquals(fetched.getTitle(), newTitle); - } - - @Test - public void should_delete_item() { - itemRepository.save(item); - - itemRepository.remove(item); - Assertions.assertFalse(itemRepository.findById(item.getId()).isPresent()); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/infrastructure/service/DefaultJwtServiceTest.java b/.framework/java/backend/src/test/java/io/spring/infrastructure/service/DefaultJwtServiceTest.java deleted file mode 100644 index b226170..0000000 --- a/.framework/java/backend/src/test/java/io/spring/infrastructure/service/DefaultJwtServiceTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.spring.infrastructure.service; - -import io.spring.core.service.JwtService; -import io.spring.core.user.User; -import java.util.Optional; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -public class DefaultJwtServiceTest { - - private JwtService jwtService; - - @BeforeEach - public void setUp() { - jwtService = - new DefaultJwtService("123123123123123123123123123123123123123123123123123123123123", 3600); - } - - @Test - public void should_generate_and_parse_token() { - User user = new User("email@email.com", "username", "123", "", ""); - String token = jwtService.toToken(user); - Assertions.assertNotNull(token); - Optional optional = jwtService.getSubFromToken(token); - Assertions.assertTrue(optional.isPresent()); - Assertions.assertEquals(optional.get(), user.getId()); - } - - @Test - public void should_get_null_with_wrong_jwt() { - Optional optional = jwtService.getSubFromToken("123"); - Assertions.assertFalse(optional.isPresent()); - } - - @Test - public void should_get_null_with_expired_jwt() { - String token = - "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhaXNlbnNpeSIsImV4cCI6MTUwMjE2MTIwNH0.SJB-U60WzxLYNomqLo4G3v3LzFxJKuVrIud8D8Lz3-mgpo9pN1i7C8ikU_jQPJGm8HsC1CquGMI-rSuM7j6LDA"; - Assertions.assertFalse(jwtService.getSubFromToken(token).isPresent()); - } -} diff --git a/.framework/java/backend/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java b/.framework/java/backend/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java deleted file mode 100644 index 8d4aaa2..0000000 --- a/.framework/java/backend/src/test/java/io/spring/infrastructure/user/MyBatisUserRepositoryTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.spring.infrastructure.user; - -import io.spring.core.user.FollowRelation; -import io.spring.core.user.User; -import io.spring.core.user.UserRepository; -import io.spring.infrastructure.DbTestBase; -import io.spring.infrastructure.repository.MyBatisUserRepository; -import java.util.Optional; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; - -@Import(MyBatisUserRepository.class) -public class MyBatisUserRepositoryTest extends DbTestBase { - @Autowired private UserRepository userRepository; - private User user; - - @BeforeEach - public void setUp() { - user = new User("aisensiy@16322.com", "aisensiy999", "1234", "", ""); - } - - @Test - public void should_save_and_fetch_user_success() { - userRepository.save(user); - Optional userOptional = userRepository.findByUsername("aisensiy999"); - Assertions.assertEquals(userOptional.get(), user); - Optional userOptional2 = userRepository.findByEmail("aisensiy@16322.com"); - Assertions.assertEquals(userOptional2.get(), user); - } - - @Test - public void should_update_user_success() { - String newEmail = "newemail@email.com"; - user.update(newEmail, "aa", "bb", "cc", "dd"); - userRepository.save(user); - Optional optional = userRepository.findByUsername(user.getUsername()); - Assertions.assertTrue(optional.isPresent()); - Assertions.assertEquals(optional.get().getEmail(), newEmail); - - String newUsername = "newUsername"; - user.update("", newUsername, "", "", ""); - userRepository.save(user); - optional = userRepository.findByEmail(user.getEmail()); - Assertions.assertTrue(optional.isPresent()); - Assertions.assertEquals(optional.get().getUsername(), newUsername); - Assertions.assertEquals(optional.get().getImage(), user.getImage()); - } - - @Test - public void should_create_new_user_follow_success() { - User other = new User("other@example.com", "other", "123", "", ""); - userRepository.save(other); - - FollowRelation followRelation = new FollowRelation(user.getId(), other.getId()); - userRepository.saveRelation(followRelation); - Assertions.assertTrue(userRepository.findRelation(user.getId(), other.getId()).isPresent()); - } - - @Test - public void should_unfollow_user_success() { - User other = new User("other@example.com", "other", "123", "", ""); - userRepository.save(other); - - FollowRelation followRelation = new FollowRelation(user.getId(), other.getId()); - userRepository.saveRelation(followRelation); - - userRepository.removeRelation(followRelation); - Assertions.assertFalse(userRepository.findRelation(user.getId(), other.getId()).isPresent()); - } -} diff --git a/.framework/java/backend/start.sh b/.framework/java/backend/start.sh deleted file mode 100755 index b405727..0000000 --- a/.framework/java/backend/start.sh +++ /dev/null @@ -1,6 +0,0 @@ -./gradlew -i bootRun & - -while true; do - inotifywait -e modify,create,delete,move -r ./src/ && \ - ./gradlew -i assemble -done diff --git a/.framework/java/charts/templates/anythink-backend-deployment.yaml b/.framework/java/charts/templates/anythink-backend-deployment.yaml deleted file mode 100644 index c03d3cc..0000000 --- a/.framework/java/charts/templates/anythink-backend-deployment.yaml +++ /dev/null @@ -1,70 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: {{ .Values.backend.serviceName }} - name: {{ .Values.backend.serviceName }} -spec: - replicas: {{ .Values.backend.replicaCount }} - selector: - matchLabels: - app: {{ .Values.backend.serviceName }} - strategy: - type: Recreate - template: - metadata: - labels: - app: {{ .Values.backend.serviceName }} - date: {{ now | unixEpoch | quote }} - spec: - containers: - - args: - - sh - - -c - - "gradlew bootRun" - env: - - name: APP_ENV - value: dev - - name: SECRET_KEY - value: e6F9KvSDf4dyXj - - name: DEBUG - value: "True" - - name: DATABASE_URL - value: "{{ .Values.database.connectionProtocol }}{{ .Values.database.env.password }}:@{{ .Values.database.serviceName }}:{{ .Values.database.servicePort }}/{{ .Values.database.databaseName }}" - image: "{{ include "anythink-tenant.backendRepository" .}}:{{ .Values.backend.image.tag }}" - imagePullPolicy: {{ .Values.backend.image.pullPolicy }} - name: {{ .Values.backend.serviceName }} - ports: - - containerPort: {{ .Values.backend.containerPort }} - name: http - protocol: TCP - startupProbe: - httpGet: - path: /health - port: http - failureThreshold: 30 - periodSeconds: 10 - livenessProbe: - httpGet: - path: /health - port: http - readinessProbe: - httpGet: - path: /health - port: http - resources: - {{- toYaml .Values.backend.resources | nindent 12 }} - initContainers: - env: - - name: APP_ENV - value: dev - - name: SECRET_KEY - value: secret - - name: DEBUG - value: "True" - - name: DATABASE_URL - value: "{{ .Values.database.connectionProtocol}}{{ .Values.database.env.password}}:@{{ .Values.database.serviceName }}:{{ .Values.database.servicePort }}/{{ .Values.database.databaseName }}" - image: "{{ include "anythink-tenant.backendRepository" .}}:{{ .Values.backend.image.tag }}" - imagePullPolicy: {{ .Values.backend.image.pullPolicy }} - name: db-migrations - restartPolicy: Always diff --git a/.framework/java/charts/templates/database-deployment.yaml b/.framework/java/charts/templates/database-deployment.yaml deleted file mode 100644 index 19752ee..0000000 --- a/.framework/java/charts/templates/database-deployment.yaml +++ /dev/null @@ -1,44 +0,0 @@ -{{- if .Values.database.deploy }} -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: {{ .Values.database.serviceName }} - name: {{ .Values.database.serviceName }} -spec: - replicas: 1 - selector: - matchLabels: - app: {{ .Values.database.serviceName }} - strategy: - type: Recreate - template: - metadata: - labels: - app: {{ .Values.database.serviceName }} - spec: - containers: - - image: "{{ .Values.database.image.repository }}:{{ .Values.database.image.tag }}" - name: {{ .Values.database.serviceName }} - env: - - name: POSTGRES_HOST_AUTH_METHOD - value: trust - - name: POSTGRES_USER - value: {{ .Values.database.env.userName }} - - name: POSTGRES_PASSWORD - value: {{ .Values.database.env.password }} - - name: POSTGRES_DB - value: {{ .Values.database.databaseName }} - imagePullPolicy: {{ .Values.database.image.pullPolicy }} - ports: - - containerPort: {{ .Values.database.containerPort }} - resources: {} - volumeMounts: - - mountPath: /data/db - name: {{ .Values.database.serviceName }}-0 - restartPolicy: Always - volumes: - - name: {{ .Values.database.serviceName }}-0 - persistentVolumeClaim: - claimName: {{ .Values.database.serviceName }}-0 -{{- end }} diff --git a/.framework/java/charts/values.yaml b/.framework/java/charts/values.yaml deleted file mode 100644 index 0464a04..0000000 --- a/.framework/java/charts/values.yaml +++ /dev/null @@ -1,70 +0,0 @@ -clusterEnv: "" -productionBackendHost: "prod.anythink.market" -stagingBackendHost: "staging.anythink.market" - -backend: - serviceName: anythink-backend - containerPort: 3000 - replicaCount: 1 - service: - type: ClusterIP - port: 80 - image: - repository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/anythink-backend - stagingRepository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/staging-anythink-backend - pullPolicy: Always - # Overrides the image tag whose default is the chart appVersion. - tag: "latest" - resources: - limits: - cpu: 100m - memory: 512Mi - requests: - cpu: 100m - memory: 128Mi - -frontend: - serviceName: anythink-frontend - containerPort: 3001 - replicaCount: 1 - service: - type: ClusterIP - port: 80 - image: - repository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/anythink-frontend - stagingRepository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/staging-anythink-frontend - pullPolicy: Always - # Overrides the image tag whose default is the chart appVersion. - tag: "latest" - resources: - limits: - cpu: 600m - memory: 768Mi - requests: - cpu: 100m - memory: 128Mi - -database: - deploy: true - connectionProtocol: postgresql:// - serviceName: postgres-python - containerPort: 5433 - servicePort: 5432 - databaseName: anythink-market - replicaCount: 1 - env: - password: postgres - service: - type: ClusterIP - port: 80 - image: - repository: postgres - pullPolicy: IfNotPresent - tag: "latest" - resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi diff --git a/.framework/java/docker-compose.yml b/.framework/java/docker-compose.yml deleted file mode 100644 index d55ba0f..0000000 --- a/.framework/java/docker-compose.yml +++ /dev/null @@ -1,59 +0,0 @@ -services: - anythink-backend-java: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-backend-java:latest - container_name: anythink-backend-java - command: sh -c "cd backend && /wait-for-it.sh postgres-java:5432 -q -t 60 && ./start.sh" - - environment: - - PORT=3000 - - GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN=${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN} - working_dir: /usr/src - volumes: - - ./:/usr/src/ - - /usr/src/backend/node_modules - ports: - - "3000:3000" - depends_on: - - "postgres-java" - - anythink-frontend-react: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-frontend-react:latest - container_name: anythink-frontend-react - command: sh -c "cd frontend && /wait-for-it.sh anythink-backend-java:3000 -t 120 --strict -- curl --head -X GET --retry 30 --retry-connrefused --retry-delay 1 anythink-backend-java:3000/api/ping && yarn start" - environment: - - NODE_ENV=development - - PORT=3001 - - REACT_APP_BACKEND_URL=${CODESPACE_BACKEND_URL:-http://localhost:3000} - - WDS_SOCKET_PORT=${CODESPACE_WDS_SOCKET_PORT:-3001} - working_dir: /usr/src - volumes: - - ./:/usr/src/ - - /usr/src/frontend/node_modules - ports: - - "3001:3001" - depends_on: - - "anythink-backend-java" - - postgres-java: - container_name: postgres-java - restart: on-failure - image: postgres - logging: - driver: none - environment: - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: postgres - POSTGRES_DB: anythink-market - volumes: - - ~/postgres/data:/data/db - ports: - - '5433:5432' - - anythink-ack: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-ack:latest - container_name: anythink-ack - environment: - - GITHUB_TOKEN=$GITHUB_TOKEN - - CODESPACE_NAME=$CODESPACE_NAME - depends_on: - - "anythink-frontend-react" diff --git a/.framework/node/.devcontainer/devcontainer.json b/.framework/node/.devcontainer/devcontainer.json deleted file mode 100644 index 171e2d2..0000000 --- a/.framework/node/.devcontainer/devcontainer.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Anythink Development Container", - "image": "public.ecr.aws/v0a2l7y2/wilco/anythink-devcontainer:latest" -} diff --git a/.framework/node/charts/.helmignore b/.framework/node/charts/.helmignore deleted file mode 100644 index 0e8a0eb..0000000 --- a/.framework/node/charts/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/.framework/node/charts/Chart.yaml b/.framework/node/charts/Chart.yaml deleted file mode 100644 index b2beb19..0000000 --- a/.framework/node/charts/Chart.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v2 -name: app -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "1.16.0" diff --git a/.framework/node/charts/templates/_helpers.yml b/.framework/node/charts/templates/_helpers.yml deleted file mode 100644 index 49515f2..0000000 --- a/.framework/node/charts/templates/_helpers.yml +++ /dev/null @@ -1,24 +0,0 @@ -{{- define "anythink-tenant.backendHost" -}} - https://{{- .Release.Namespace }}-api. - {{- if eq .Values.clusterEnv "staging" }} - {{- .Values.stagingBackendHost }} - {{- else }} - {{- .Values.productionBackendHost }} - {{- end }} -{{- end }} - -{{- define "anythink-tenant.backendRepository" -}} - {{- if eq .Values.clusterEnv "staging" }} - {{- .Values.backend.image.stagingRepository }} - {{- else }} - {{- .Values.backend.image.repository }} - {{- end }} -{{- end }} - -{{- define "anythink-tenant.frontendRepository" -}} - {{- if eq .Values.clusterEnv "staging" }} - {{- .Values.frontend.image.stagingRepository }} - {{- else }} - {{- .Values.frontend.image.repository }} - {{- end }} -{{- end }} diff --git a/.framework/node/charts/templates/anythink-backend-service.yaml b/.framework/node/charts/templates/anythink-backend-service.yaml deleted file mode 100644 index 21bb516..0000000 --- a/.framework/node/charts/templates/anythink-backend-service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - creationTimestamp: null - labels: - app: {{ .Values.backend.serviceName }} - name: {{ .Values.backend.serviceName }} -spec: - ports: - - name: "{{ .Values.backend.containerPort }}" - port: {{ .Values.backend.containerPort }} - targetPort: {{ .Values.backend.containerPort }} - selector: - app: {{ .Values.backend.serviceName }} diff --git a/.framework/node/charts/templates/anythink-frontend-deployment.yaml b/.framework/node/charts/templates/anythink-frontend-deployment.yaml deleted file mode 100644 index f9be249..0000000 --- a/.framework/node/charts/templates/anythink-frontend-deployment.yaml +++ /dev/null @@ -1,55 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: {{ .Values.frontend.serviceName }} - name: {{ .Values.frontend.serviceName }} -spec: - replicas: {{ .Values.frontend.replicaCount }} - selector: - matchLabels: - app: {{ .Values.frontend.serviceName }} - strategy: - type: Recreate - template: - metadata: - labels: - app: {{ .Values.frontend.serviceName }} - date: {{ now | unixEpoch | quote }} - spec: - containers: - - args: - - sh - - -c - - yarn start - env: - - name: NODE_ENV - value: development - - name: PORT - value: "{{ .Values.frontend.containerPort }}" - - name: REACT_APP_BACKEND_URL - value: {{ include "anythink-tenant.backendHost" .}} - image: "{{ include "anythink-tenant.frontendRepository" .}}:{{ .Values.frontend.image.tag }}" - imagePullPolicy: {{ .Values.frontend.image.pullPolicy }} - name: {{ .Values.frontend.serviceName }} - ports: - - containerPort: {{ .Values.frontend.containerPort }} - name: http - protocol: TCP - startupProbe: - httpGet: - path: / - port: http - failureThreshold: 30 - periodSeconds: 10 - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {{- toYaml .Values.frontend.resources | nindent 12 }} - restartPolicy: Always diff --git a/.framework/node/charts/templates/anythink-frontend-service.yaml b/.framework/node/charts/templates/anythink-frontend-service.yaml deleted file mode 100644 index 217f8c5..0000000 --- a/.framework/node/charts/templates/anythink-frontend-service.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: {{ .Values.frontend.serviceName }} - name: {{ .Values.frontend.serviceName }} -spec: - ports: - - name: "{{ .Values.frontend.containerPort }}" - port: {{ .Values.frontend.containerPort }} - targetPort: {{ .Values.frontend.containerPort }} - selector: - app: {{ .Values.frontend.serviceName }} diff --git a/.framework/node/charts/templates/database-pvc.yaml b/.framework/node/charts/templates/database-pvc.yaml deleted file mode 100644 index 88517f3..0000000 --- a/.framework/node/charts/templates/database-pvc.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - labels: - app: {{ .Values.database.serviceName }}-0 - name: {{ .Values.database.serviceName }}-0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Mi diff --git a/.framework/node/charts/templates/database-service.yaml b/.framework/node/charts/templates/database-service.yaml deleted file mode 100644 index 80b47d3..0000000 --- a/.framework/node/charts/templates/database-service.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: {{ .Values.database.serviceName }} - name: {{ .Values.database.serviceName }} -spec: - ports: - - name: "{{ .Values.database.servicePort }}" - port: {{ .Values.database.servicePort }} - targetPort: {{ .Values.database.servicePort }} - selector: - app: {{ .Values.database.serviceName }} diff --git a/.framework/python/.devcontainer/devcontainer.json b/.framework/python/.devcontainer/devcontainer.json deleted file mode 100644 index 171e2d2..0000000 --- a/.framework/python/.devcontainer/devcontainer.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Anythink Development Container", - "image": "public.ecr.aws/v0a2l7y2/wilco/anythink-devcontainer:latest" -} diff --git a/.framework/python/backend/.dockerignore b/.framework/python/backend/.dockerignore deleted file mode 100644 index 238473f..0000000 --- a/.framework/python/backend/.dockerignore +++ /dev/null @@ -1,20 +0,0 @@ -__pycache__ -*.pyc -*.pyo -*.pyd -.Python -.env* -pip-log.txt -pip-delete-this-directory.txt -.tox -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -*.log -.git* -scripts -postman -./postgres-data diff --git a/.framework/python/backend/.gitignore b/.framework/python/backend/.gitignore deleted file mode 100644 index ab61e76..0000000 --- a/.framework/python/backend/.gitignore +++ /dev/null @@ -1,110 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -.idea/ -.vscode/ - -# Project -postgres-data diff --git a/.framework/python/backend/Dockerfile.aws b/.framework/python/backend/Dockerfile.aws deleted file mode 100644 index 3ee3974..0000000 --- a/.framework/python/backend/Dockerfile.aws +++ /dev/null @@ -1,15 +0,0 @@ -FROM python:3.9.13 - -ENV VIRTUAL_ENV=/opt/venv -RUN python3 -m venv $VIRTUAL_ENV -ENV PATH="$VIRTUAL_ENV/bin:$PATH" - -RUN pip install poetry==1.2.0 - -# Pre-install poetry packages -WORKDIR /usr/src -COPY backend ./backend -COPY .wilco ./.wilco -WORKDIR /usr/src/backend -RUN poetry install -RUN poetry export -f "requirements.txt" --without-hashes --with-credentials > "requirements.txt" diff --git a/.framework/python/backend/LICENSE b/.framework/python/backend/LICENSE deleted file mode 100644 index 173d6d0..0000000 --- a/.framework/python/backend/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Nik Sidnev - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -SELLERS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/.framework/python/backend/README.md b/.framework/python/backend/README.md deleted file mode 100644 index 3a6fc73..0000000 --- a/.framework/python/backend/README.md +++ /dev/null @@ -1,30 +0,0 @@ -Web routes -========== - -All routes are available on `/docs` or `/redoc` paths with Swagger or ReDoc. - -Project structure -================= - -Files related to application are in the `app` or `tests` directories. Application parts are: - - app - ├── api - web related stuff. - │   ├── dependencies - dependencies for routes definition. - │   ├── errors - definition of error handlers. - │   └── routes - web routes. - ├── core - application configuration, startup events, logging. - ├── db - db related stuff. - │   ├── migrations - manually written alembic migrations. - │   └── repositories - all crud stuff. - ├── models - pydantic models for this application. - │   ├── domain - main models that are used almost everywhere. - │   └── schemas - schemas for using in web routes. - ├── resources - strings that are used in web responses. - ├── services - logic that is not just crud related. - └── main.py - FastAPI application creation and configuration. - -Project structure -================= - -Project dependencies are managed by poetry (https://python-poetry.org), using venv (https://docs.python.org/3/library/venv.html). diff --git a/.framework/python/backend/alembic.ini b/.framework/python/backend/alembic.ini deleted file mode 100644 index 2c43e60..0000000 --- a/.framework/python/backend/alembic.ini +++ /dev/null @@ -1,36 +0,0 @@ -[alembic] -script_location = ./app/db/migrations - -[loggers] -keys = root,sqlalchemy,alembic - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S diff --git a/.framework/python/backend/app/__init__.py b/.framework/python/backend/app/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/api/__init__.py b/.framework/python/backend/app/api/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/api/dependencies/__init__.py b/.framework/python/backend/app/api/dependencies/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/api/dependencies/authentication.py b/.framework/python/backend/app/api/dependencies/authentication.py deleted file mode 100644 index dbc8d48..0000000 --- a/.framework/python/backend/app/api/dependencies/authentication.py +++ /dev/null @@ -1,111 +0,0 @@ -# noqa:WPS201 -from typing import Callable, Optional - -from fastapi import Depends, HTTPException, Security -from fastapi.security import APIKeyHeader -from starlette import requests, status -from starlette.exceptions import HTTPException as StarletteHTTPException - -from app.api.dependencies.database import get_repository -from app.core.config import get_app_settings -from app.core.settings.app import AppSettings -from app.db.errors import EntityDoesNotExist -from app.db.repositories.users import UsersRepository -from app.models.domain.users import User -from app.resources import strings -from app.services import jwt - -HEADER_KEY = "Authorization" - - -class RWAPIKeyHeader(APIKeyHeader): - async def __call__( # noqa: WPS610 - self, - request: requests.Request, - ) -> Optional[str]: - try: - return await super().__call__(request) - except StarletteHTTPException as original_auth_exc: - raise HTTPException( - status_code=original_auth_exc.status_code, - detail=strings.AUTHENTICATION_REQUIRED, - ) - - -def get_current_user_authorizer(*, required: bool = True) -> Callable: # type: ignore - return _get_current_user if required else _get_current_user_optional - - -def _get_authorization_header_retriever( - *, - required: bool = True, -) -> Callable: # type: ignore - return _get_authorization_header if required else _get_authorization_header_optional - - -def _get_authorization_header( - api_key: str = Security(RWAPIKeyHeader(name=HEADER_KEY)), - settings: AppSettings = Depends(get_app_settings), -) -> str: - try: - token_prefix, token = api_key.split(" ") - except ValueError: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=strings.WRONG_TOKEN_PREFIX, - ) - if token_prefix != settings.jwt_token_prefix: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=strings.WRONG_TOKEN_PREFIX, - ) - - return token - - -def _get_authorization_header_optional( - authorization: Optional[str] = Security( - RWAPIKeyHeader(name=HEADER_KEY, auto_error=False), - ), - settings: AppSettings = Depends(get_app_settings), -) -> str: - if authorization: - return _get_authorization_header(authorization, settings) - - return "" - - -async def _get_current_user( - users_repo: UsersRepository = Depends(get_repository(UsersRepository)), - token: str = Depends(_get_authorization_header_retriever()), - settings: AppSettings = Depends(get_app_settings), -) -> User: - try: - username = jwt.get_username_from_token( - token, - str(settings.secret_key.get_secret_value()), - ) - except ValueError: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=strings.MALFORMED_PAYLOAD, - ) - - try: - return await users_repo.get_user_by_username(username=username) - except EntityDoesNotExist: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=strings.MALFORMED_PAYLOAD, - ) - - -async def _get_current_user_optional( - repo: UsersRepository = Depends(get_repository(UsersRepository)), - token: str = Depends(_get_authorization_header_retriever(required=False)), - settings: AppSettings = Depends(get_app_settings), -) -> Optional[User]: - if token: - return await _get_current_user(repo, token, settings) - - return None diff --git a/.framework/python/backend/app/api/dependencies/comments.py b/.framework/python/backend/app/api/dependencies/comments.py deleted file mode 100644 index 07073b7..0000000 --- a/.framework/python/backend/app/api/dependencies/comments.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import Optional - -from fastapi import Depends, HTTPException, Path -from starlette import status - -from app.api.dependencies import items, authentication, database -from app.db.errors import EntityDoesNotExist -from app.db.repositories.comments import CommentsRepository -from app.models.domain.items import Item -from app.models.domain.comments import Comment -from app.models.domain.users import User -from app.resources import strings - - -async def get_comment_by_id_from_path( - comment_id: int = Path(..., ge=1), - item: Item = Depends(items.get_item_by_slug_from_path), - user: Optional[User] = Depends( - authentication.get_current_user_authorizer(required=False), - ), - comments_repo: CommentsRepository = Depends( - database.get_repository(CommentsRepository), - ), -) -> Comment: - try: - return await comments_repo.get_comment_by_id( - comment_id=comment_id, - item=item, - user=user, - ) - except EntityDoesNotExist: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=strings.COMMENT_DOES_NOT_EXIST, - ) - - diff --git a/.framework/python/backend/app/api/dependencies/database.py b/.framework/python/backend/app/api/dependencies/database.py deleted file mode 100644 index cc91306..0000000 --- a/.framework/python/backend/app/api/dependencies/database.py +++ /dev/null @@ -1,30 +0,0 @@ -from typing import AsyncGenerator, Callable, Type - -from asyncpg.connection import Connection -from asyncpg.pool import Pool -from fastapi import Depends -from starlette.requests import Request - -from app.db.repositories.base import BaseRepository - - -def _get_db_pool(request: Request) -> Pool: - return request.app.state.pool - - -async def _get_connection_from_pool( - pool: Pool = Depends(_get_db_pool), -) -> AsyncGenerator[Connection, None]: - async with pool.acquire() as conn: - yield conn - - -def get_repository( - repo_type: Type[BaseRepository], -) -> Callable[[Connection], BaseRepository]: - def _get_repo( - conn: Connection = Depends(_get_connection_from_pool), - ) -> BaseRepository: - return repo_type(conn) - - return _get_repo diff --git a/.framework/python/backend/app/api/dependencies/items.py b/.framework/python/backend/app/api/dependencies/items.py deleted file mode 100644 index 8688c96..0000000 --- a/.framework/python/backend/app/api/dependencies/items.py +++ /dev/null @@ -1,59 +0,0 @@ -from typing import Optional - -from fastapi import Depends, HTTPException, Path, Query -from starlette import status - -from app.api.dependencies.authentication import get_current_user_authorizer -from app.api.dependencies.database import get_repository -from app.db.errors import EntityDoesNotExist -from app.db.repositories.items import ItemsRepository -from app.models.domain.items import Item -from app.models.domain.users import User -from app.models.schemas.items import ( - DEFAULT_ITEMS_LIMIT, - DEFAULT_ITEMS_OFFSET, - ItemsFilters, -) -from app.resources import strings -from app.services.items import check_user_can_modify_item - - -def get_items_filters( - tag: Optional[str] = None, - seller: Optional[str] = None, - favorited: Optional[str] = None, - limit: int = Query(DEFAULT_ITEMS_LIMIT, ge=1), - offset: int = Query(DEFAULT_ITEMS_OFFSET, ge=0), -) -> ItemsFilters: - return ItemsFilters( - tag=tag, - seller=seller, - favorited=favorited, - limit=limit, - offset=offset, - ) - - -async def get_item_by_slug_from_path( - slug: str = Path(..., min_length=1), - user: Optional[User] = Depends(get_current_user_authorizer(required=False)), - items_repo: ItemsRepository = Depends(get_repository(ItemsRepository)), -) -> Item: - try: - return await items_repo.get_item_by_slug(slug=slug, requested_user=user) - except EntityDoesNotExist: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=strings.ITEM_DOES_NOT_EXIST_ERROR, - ) - - -def check_item_modification_permissions( - current_item: Item = Depends(get_item_by_slug_from_path), - user: User = Depends(get_current_user_authorizer()), -) -> None: - if not check_user_can_modify_item(current_item, user): - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=strings.USER_IS_NOT_SELLER_OF_ITEM, - ) diff --git a/.framework/python/backend/app/api/dependencies/profiles.py b/.framework/python/backend/app/api/dependencies/profiles.py deleted file mode 100644 index db8f9b0..0000000 --- a/.framework/python/backend/app/api/dependencies/profiles.py +++ /dev/null @@ -1,29 +0,0 @@ -from typing import Optional - -from fastapi import Depends, HTTPException, Path -from starlette.status import HTTP_404_NOT_FOUND - -from app.api.dependencies.authentication import get_current_user_authorizer -from app.api.dependencies.database import get_repository -from app.db.errors import EntityDoesNotExist -from app.db.repositories.profiles import ProfilesRepository -from app.models.domain.profiles import Profile -from app.models.domain.users import User -from app.resources import strings - - -async def get_profile_by_username_from_path( - username: str = Path(..., min_length=1), - user: Optional[User] = Depends(get_current_user_authorizer(required=False)), - profiles_repo: ProfilesRepository = Depends(get_repository(ProfilesRepository)), -) -> Profile: - try: - return await profiles_repo.get_profile_by_username( - username=username, - requested_user=user, - ) - except EntityDoesNotExist: - raise HTTPException( - status_code=HTTP_404_NOT_FOUND, - detail=strings.USER_DOES_NOT_EXIST_ERROR, - ) diff --git a/.framework/python/backend/app/api/errors/__init__.py b/.framework/python/backend/app/api/errors/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/api/errors/http_error.py b/.framework/python/backend/app/api/errors/http_error.py deleted file mode 100644 index c503229..0000000 --- a/.framework/python/backend/app/api/errors/http_error.py +++ /dev/null @@ -1,7 +0,0 @@ -from fastapi import HTTPException -from starlette.requests import Request -from starlette.responses import JSONResponse - - -async def http_error_handler(_: Request, exc: HTTPException) -> JSONResponse: - return JSONResponse({"errors": [exc.detail]}, status_code=exc.status_code) diff --git a/.framework/python/backend/app/api/errors/validation_error.py b/.framework/python/backend/app/api/errors/validation_error.py deleted file mode 100644 index a85730c..0000000 --- a/.framework/python/backend/app/api/errors/validation_error.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Union - -from fastapi.exceptions import RequestValidationError -from fastapi.openapi.constants import REF_PREFIX -from fastapi.openapi.utils import validation_error_response_definition -from pydantic import ValidationError -from starlette.requests import Request -from starlette.responses import JSONResponse -from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY - - -async def http422_error_handler( - _: Request, - exc: Union[RequestValidationError, ValidationError], -) -> JSONResponse: - return JSONResponse( - {"errors": exc.errors()}, - status_code=HTTP_422_UNPROCESSABLE_ENTITY, - ) - - -validation_error_response_definition["properties"] = { - "errors": { - "title": "Errors", - "type": "array", - "items": {"$ref": "{0}ValidationError".format(REF_PREFIX)}, - }, -} diff --git a/.framework/python/backend/app/api/routes/__init__.py b/.framework/python/backend/app/api/routes/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/api/routes/api.py b/.framework/python/backend/app/api/routes/api.py deleted file mode 100644 index 7404d51..0000000 --- a/.framework/python/backend/app/api/routes/api.py +++ /dev/null @@ -1,17 +0,0 @@ -from fastapi import APIRouter - -from app.api.routes import authentication, comments, profiles, tags, users, ping -from app.api.routes.items import api as items - -router = APIRouter() -router.include_router(ping.router, prefix="/ping") -router.include_router(authentication.router, tags=["authentication"], prefix="/users") -router.include_router(users.router, tags=["users"], prefix="/user") -router.include_router(profiles.router, tags=["profiles"], prefix="/profiles") -router.include_router(items.router, tags=["items"]) -router.include_router( - comments.router, - tags=["comments"], - prefix="/items/{slug}/comments", -) -router.include_router(tags.router, tags=["tags"], prefix="/tags") diff --git a/.framework/python/backend/app/api/routes/authentication.py b/.framework/python/backend/app/api/routes/authentication.py deleted file mode 100644 index 35d7322..0000000 --- a/.framework/python/backend/app/api/routes/authentication.py +++ /dev/null @@ -1,97 +0,0 @@ -from fastapi import APIRouter, Body, Depends, HTTPException -from starlette.status import HTTP_201_CREATED, HTTP_400_BAD_REQUEST - -from app.api.dependencies.database import get_repository -from app.core.config import get_app_settings -from app.core.settings.app import AppSettings -from app.db.errors import EntityDoesNotExist -from app.db.repositories.users import UsersRepository -from app.models.schemas.users import ( - UserInCreate, - UserInLogin, - UserInResponse, - UserWithToken, -) -from app.resources import strings -from app.services import jwt -from app.services.authentication import check_email_is_taken, check_username_is_taken -from app.services.event import send_event - -router = APIRouter() - - -@router.post("/login", response_model=UserInResponse, name="auth:login") -async def login( - user_login: UserInLogin = Body(..., embed=True, alias="user"), - users_repo: UsersRepository = Depends(get_repository(UsersRepository)), - settings: AppSettings = Depends(get_app_settings), -) -> UserInResponse: - wrong_login_error = HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail=strings.INCORRECT_LOGIN_INPUT, - ) - - try: - user = await users_repo.get_user_by_email(email=user_login.email) - except EntityDoesNotExist as existence_error: - raise wrong_login_error from existence_error - - if not user.check_password(user_login.password): - raise wrong_login_error - - token = jwt.create_access_token_for_user( - user, - str(settings.secret_key.get_secret_value()), - ) - return UserInResponse( - user=UserWithToken( - username=user.username, - email=user.email, - bio=user.bio, - image=user.image, - token=token, - ), - ) - - -@router.post( - "", - status_code=HTTP_201_CREATED, - response_model=UserInResponse, - name="auth:register", -) -async def register( - user_create: UserInCreate = Body(..., embed=True, alias="user"), - users_repo: UsersRepository = Depends(get_repository(UsersRepository)), - settings: AppSettings = Depends(get_app_settings), -) -> UserInResponse: - if await check_username_is_taken(users_repo, user_create.username): - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail=strings.USERNAME_TAKEN, - ) - - if await check_email_is_taken(users_repo, user_create.email): - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail=strings.EMAIL_TAKEN, - ) - - user = await users_repo.create_user(**user_create.dict()) - - token = jwt.create_access_token_for_user( - user, - str(settings.secret_key.get_secret_value()), - ) - - send_event('user_created', { 'username': user.username }) - - return UserInResponse( - user=UserWithToken( - username=user.username, - email=user.email, - bio=user.bio, - image=user.image, - token=token, - ), - ) diff --git a/.framework/python/backend/app/api/routes/comments.py b/.framework/python/backend/app/api/routes/comments.py deleted file mode 100644 index 5810e3a..0000000 --- a/.framework/python/backend/app/api/routes/comments.py +++ /dev/null @@ -1,67 +0,0 @@ -from typing import Optional - -from fastapi import APIRouter, Body, Depends, Response -from starlette import status - -from app.api.dependencies.items import get_item_by_slug_from_path -from app.api.dependencies.authentication import get_current_user_authorizer -from app.api.dependencies.comments import get_comment_by_id_from_path -from app.api.dependencies.database import get_repository -from app.db.repositories.comments import CommentsRepository -from app.models.domain.items import Item -from app.models.domain.comments import Comment -from app.models.domain.users import User -from app.models.schemas.comments import ( - CommentInCreate, - CommentInResponse, - ListOfCommentsInResponse, -) - -router = APIRouter() - - -@router.get( - "", - response_model=ListOfCommentsInResponse, - name="comments:get-comments-for-item", -) -async def list_comments_for_item( - item: Item = Depends(get_item_by_slug_from_path), - user: Optional[User] = Depends(get_current_user_authorizer(required=False)), - comments_repo: CommentsRepository = Depends(get_repository(CommentsRepository)), -) -> ListOfCommentsInResponse: - comments = await comments_repo.get_comments_for_item(item=item, user=user) - return ListOfCommentsInResponse(comments=comments) - - -@router.post( - "", - status_code=status.HTTP_201_CREATED, - response_model=CommentInResponse, - name="comments:create-comment-for-item", -) -async def create_comment_for_item( - comment_create: CommentInCreate = Body(..., embed=True, alias="comment"), - item: Item = Depends(get_item_by_slug_from_path), - user: User = Depends(get_current_user_authorizer()), - comments_repo: CommentsRepository = Depends(get_repository(CommentsRepository)), -) -> CommentInResponse: - comment = await comments_repo.create_comment_for_item( - body=comment_create.body, - item=item, - user=user, - ) - return CommentInResponse(comment=comment) - - -@router.delete( - "/{comment_id}", - status_code=status.HTTP_204_NO_CONTENT, - name="comments:delete-comment-from-item", - response_class=Response, -) -async def delete_comment_from_item( - comment: Comment = Depends(get_comment_by_id_from_path), - comments_repo: CommentsRepository = Depends(get_repository(CommentsRepository)), -) -> None: - await comments_repo.delete_comment(comment=comment) diff --git a/.framework/python/backend/app/api/routes/home.py b/.framework/python/backend/app/api/routes/home.py deleted file mode 100644 index 5521b49..0000000 --- a/.framework/python/backend/app/api/routes/home.py +++ /dev/null @@ -1,11 +0,0 @@ -from fastapi import APIRouter - -router = APIRouter() - -@router.get("/", status_code=200) -async def home(): - return "Anythink backend is up." - -@router.get("/health", status_code=200) -async def health(): - return "OK" diff --git a/.framework/python/backend/app/api/routes/items/__init__.py b/.framework/python/backend/app/api/routes/items/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/api/routes/items/api.py b/.framework/python/backend/app/api/routes/items/api.py deleted file mode 100644 index 731c4e0..0000000 --- a/.framework/python/backend/app/api/routes/items/api.py +++ /dev/null @@ -1,8 +0,0 @@ -from fastapi import APIRouter - -from app.api.routes.items import items_common, items_resource - -router = APIRouter() - -router.include_router(items_common.router, prefix="/items") -router.include_router(items_resource.router, prefix="/items") diff --git a/.framework/python/backend/app/api/routes/items/items_common.py b/.framework/python/backend/app/api/routes/items/items_common.py deleted file mode 100644 index 64447b4..0000000 --- a/.framework/python/backend/app/api/routes/items/items_common.py +++ /dev/null @@ -1,104 +0,0 @@ -from fastapi import APIRouter, Depends, HTTPException, Query -from starlette import status - -from app.api.dependencies.items import get_item_by_slug_from_path -from app.api.dependencies.authentication import get_current_user_authorizer -from app.api.dependencies.database import get_repository -from app.db.repositories.items import ItemsRepository -from app.models.domain.items import Item -from app.models.domain.users import User -from app.models.schemas.items import ( - DEFAULT_ITEMS_LIMIT, - DEFAULT_ITEMS_OFFSET, - ItemForResponse, - ItemInResponse, - ListOfItemsInResponse, -) -from app.resources import strings - -router = APIRouter() - - -@router.get( - "/feed", - response_model=ListOfItemsInResponse, - name="items:get-user-feed-items", -) -async def get_items_for_user_feed( - limit: int = Query(DEFAULT_ITEMS_LIMIT, ge=1), - offset: int = Query(DEFAULT_ITEMS_OFFSET, ge=0), - user: User = Depends(get_current_user_authorizer()), - items_repo: ItemsRepository = Depends(get_repository(ItemsRepository)), -) -> ListOfItemsInResponse: - items = await items_repo.get_items_for_user_feed( - user=user, - limit=limit, - offset=offset, - ) - items_for_response = [ - ItemForResponse(**item.dict()) for item in items - ] - return ListOfItemsInResponse( - items=items_for_response, - items_count=len(items), - ) - - -@router.post( - "/{slug}/favorite", - response_model=ItemInResponse, - name="items:mark-item-favorite", -) -async def mark_item_as_favorite( - item: Item = Depends(get_item_by_slug_from_path), - user: User = Depends(get_current_user_authorizer()), - items_repo: ItemsRepository = Depends(get_repository(ItemsRepository)), -) -> ItemInResponse: - if not item.favorited: - await items_repo.add_item_into_favorites(item=item, user=user) - - return ItemInResponse( - item=ItemForResponse.from_orm( - item.copy( - update={ - "favorited": True, - "favorites_count": item.favorites_count + 1, - }, - ), - ), - ) - - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=strings.ITEM_IS_ALREADY_FAVORITED, - ) - - -@router.delete( - "/{slug}/favorite", - response_model=ItemInResponse, - name="items:unmark-item-favorite", -) -async def remove_item_from_favorites( - item: Item = Depends(get_item_by_slug_from_path), - user: User = Depends(get_current_user_authorizer()), - items_repo: ItemsRepository = Depends(get_repository(ItemsRepository)), -) -> ItemInResponse: - if item.favorited: - await items_repo.remove_item_from_favorites(item=item, user=user) - - return ItemInResponse( - item=ItemForResponse.from_orm( - item.copy( - update={ - "favorited": False, - "favorites_count": item.favorites_count - 1, - }, - ), - ), - ) - - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=strings.ITEM_IS_NOT_FAVORITED, - ) diff --git a/.framework/python/backend/app/api/routes/items/items_resource.py b/.framework/python/backend/app/api/routes/items/items_resource.py deleted file mode 100644 index e396091..0000000 --- a/.framework/python/backend/app/api/routes/items/items_resource.py +++ /dev/null @@ -1,122 +0,0 @@ -from typing import Optional - -from fastapi import APIRouter, Body, Depends, HTTPException, Response -from starlette import status - -from app.api.dependencies.items import ( - check_item_modification_permissions, - get_item_by_slug_from_path, - get_items_filters, -) -from app.api.dependencies.authentication import get_current_user_authorizer -from app.api.dependencies.database import get_repository -from app.db.repositories.items import ItemsRepository -from app.models.domain.items import Item -from app.models.domain.users import User -from app.models.schemas.items import ( - ItemForResponse, - ItemInCreate, - ItemInResponse, - ItemInUpdate, - ItemsFilters, - ListOfItemsInResponse, -) -from app.resources import strings -from app.services.items import check_item_exists, get_slug_for_item -from app.services.event import send_event - -router = APIRouter() - - -@router.get("", response_model=ListOfItemsInResponse, name="items:list-items") -async def list_items( - items_filters: ItemsFilters = Depends(get_items_filters), - user: Optional[User] = Depends(get_current_user_authorizer(required=False)), - items_repo: ItemsRepository = Depends(get_repository(ItemsRepository)), -) -> ListOfItemsInResponse: - items = await items_repo.filter_items( - tag=items_filters.tag, - seller=items_filters.seller, - favorited=items_filters.favorited, - limit=items_filters.limit, - offset=items_filters.offset, - requested_user=user, - ) - items_for_response = [ - ItemForResponse.from_orm(item) for item in items - ] - return ListOfItemsInResponse( - items=items_for_response, - items_count=len(items), - ) - - -@router.post( - "", - status_code=status.HTTP_201_CREATED, - response_model=ItemInResponse, - name="items:create-item", -) -async def create_new_item( - item_create: ItemInCreate = Body(..., embed=True, alias="item"), - user: User = Depends(get_current_user_authorizer()), - items_repo: ItemsRepository = Depends(get_repository(ItemsRepository)), -) -> ItemInResponse: - slug = get_slug_for_item(item_create.title) - if await check_item_exists(items_repo, slug): - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=strings.ITEM_ALREADY_EXISTS, - ) - item = await items_repo.create_item( - slug=slug, - title=item_create.title, - description=item_create.description, - body=item_create.body, - seller=user, - tags=item_create.tags, - image=item_create.image - ) - send_event('item_created', {'item': item_create.title}) - return ItemInResponse(item=ItemForResponse.from_orm(item)) - - -@router.get("/{slug}", response_model=ItemInResponse, name="items:get-item") -async def retrieve_item_by_slug( - item: Item = Depends(get_item_by_slug_from_path), -) -> ItemInResponse: - return ItemInResponse(item=ItemForResponse.from_orm(item)) - - -@router.put( - "/{slug}", - response_model=ItemInResponse, - name="items:update-item", - dependencies=[Depends(check_item_modification_permissions)], -) -async def update_item_by_slug( - item_update: ItemInUpdate = Body(..., embed=True, alias="item"), - current_item: Item = Depends(get_item_by_slug_from_path), - items_repo: ItemsRepository = Depends(get_repository(ItemsRepository)), -) -> ItemInResponse: - slug = get_slug_for_item(item_update.title) if item_update.title else None - item = await items_repo.update_item( - item=current_item, - slug=slug, - **item_update.dict(), - ) - return ItemInResponse(item=ItemForResponse.from_orm(item)) - - -@router.delete( - "/{slug}", - status_code=status.HTTP_204_NO_CONTENT, - name="items:delete-item", - dependencies=[Depends(check_item_modification_permissions)], - response_class=Response, -) -async def delete_item_by_slug( - item: Item = Depends(get_item_by_slug_from_path), - items_repo: ItemsRepository = Depends(get_repository(ItemsRepository)), -) -> None: - await items_repo.delete_item(item=item) diff --git a/.framework/python/backend/app/api/routes/ping.py b/.framework/python/backend/app/api/routes/ping.py deleted file mode 100644 index 3d0f5f0..0000000 --- a/.framework/python/backend/app/api/routes/ping.py +++ /dev/null @@ -1,17 +0,0 @@ -import json -import logging - -from fastapi import APIRouter, Depends, HTTPException -from app.services.event import send_event - -router = APIRouter() - -@router.get("") -async def check_ping(): - try: - res = send_event('ping', {}) - return res.json() - - except Exception as e: - logging.error(e) - raise HTTPException(status_code=500, detail="Error") diff --git a/.framework/python/backend/app/api/routes/profiles.py b/.framework/python/backend/app/api/routes/profiles.py deleted file mode 100644 index 6ec1bf0..0000000 --- a/.framework/python/backend/app/api/routes/profiles.py +++ /dev/null @@ -1,84 +0,0 @@ -from fastapi import APIRouter, Depends, HTTPException -from starlette.status import HTTP_400_BAD_REQUEST - -from app.api.dependencies.authentication import get_current_user_authorizer -from app.api.dependencies.database import get_repository -from app.api.dependencies.profiles import get_profile_by_username_from_path -from app.db.repositories.profiles import ProfilesRepository -from app.models.domain.profiles import Profile -from app.models.domain.users import User -from app.models.schemas.profiles import ProfileInResponse -from app.resources import strings - -router = APIRouter() - - -@router.get( - "/{username}", - response_model=ProfileInResponse, - name="profiles:get-profile", -) -async def retrieve_profile_by_username( - profile: Profile = Depends(get_profile_by_username_from_path), -) -> ProfileInResponse: - return ProfileInResponse(profile=profile) - - -@router.post( - "/{username}/follow", - response_model=ProfileInResponse, - name="profiles:follow-user", -) -async def follow_for_user( - profile: Profile = Depends(get_profile_by_username_from_path), - user: User = Depends(get_current_user_authorizer()), - profiles_repo: ProfilesRepository = Depends(get_repository(ProfilesRepository)), -) -> ProfileInResponse: - if user.username == profile.username: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail=strings.UNABLE_TO_FOLLOW_YOURSELF, - ) - - if profile.following: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail=strings.USER_IS_ALREADY_FOLLOWED, - ) - - await profiles_repo.add_user_into_followers( - target_user=profile, - requested_user=user, - ) - - return ProfileInResponse(profile=profile.copy(update={"following": True})) - - -@router.delete( - "/{username}/follow", - response_model=ProfileInResponse, - name="profiles:unsubscribe-from-user", -) -async def unsubscribe_from_user( - profile: Profile = Depends(get_profile_by_username_from_path), - user: User = Depends(get_current_user_authorizer()), - profiles_repo: ProfilesRepository = Depends(get_repository(ProfilesRepository)), -) -> ProfileInResponse: - if user.username == profile.username: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail=strings.UNABLE_TO_UNSUBSCRIBE_FROM_YOURSELF, - ) - - if not profile.following: - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail=strings.USER_IS_NOT_FOLLOWED, - ) - - await profiles_repo.remove_user_from_followers( - target_user=profile, - requested_user=user, - ) - - return ProfileInResponse(profile=profile.copy(update={"following": False})) diff --git a/.framework/python/backend/app/api/routes/tags.py b/.framework/python/backend/app/api/routes/tags.py deleted file mode 100644 index 4706187..0000000 --- a/.framework/python/backend/app/api/routes/tags.py +++ /dev/null @@ -1,15 +0,0 @@ -from fastapi import APIRouter, Depends - -from app.api.dependencies.database import get_repository -from app.db.repositories.tags import TagsRepository -from app.models.schemas.tags import TagsInList - -router = APIRouter() - - -@router.get("", response_model=TagsInList, name="tags:get-all") -async def get_all_tags( - tags_repo: TagsRepository = Depends(get_repository(TagsRepository)), -) -> TagsInList: - tags = await tags_repo.get_all_tags() - return TagsInList(tags=tags) diff --git a/.framework/python/backend/app/api/routes/users.py b/.framework/python/backend/app/api/routes/users.py deleted file mode 100644 index 81bcf97..0000000 --- a/.framework/python/backend/app/api/routes/users.py +++ /dev/null @@ -1,73 +0,0 @@ -from fastapi import APIRouter, Body, Depends, HTTPException -from starlette.status import HTTP_400_BAD_REQUEST - -from app.api.dependencies.authentication import get_current_user_authorizer -from app.api.dependencies.database import get_repository -from app.core.config import get_app_settings -from app.core.settings.app import AppSettings -from app.db.repositories.users import UsersRepository -from app.models.domain.users import User -from app.models.schemas.users import UserInResponse, UserInUpdate, UserWithToken -from app.resources import strings -from app.services import jwt -from app.services.authentication import check_email_is_taken, check_username_is_taken - -router = APIRouter() - - -@router.get("", response_model=UserInResponse, name="users:get-current-user") -async def retrieve_current_user( - user: User = Depends(get_current_user_authorizer()), - settings: AppSettings = Depends(get_app_settings), -) -> UserInResponse: - token = jwt.create_access_token_for_user( - user, - str(settings.secret_key.get_secret_value()), - ) - return UserInResponse( - user=UserWithToken( - username=user.username, - email=user.email, - bio=user.bio, - image=user.image, - token=token, - ), - ) - - -@router.put("", response_model=UserInResponse, name="users:update-current-user") -async def update_current_user( - user_update: UserInUpdate = Body(..., embed=True, alias="user"), - current_user: User = Depends(get_current_user_authorizer()), - users_repo: UsersRepository = Depends(get_repository(UsersRepository)), - settings: AppSettings = Depends(get_app_settings), -) -> UserInResponse: - if user_update.username and user_update.username != current_user.username: - if await check_username_is_taken(users_repo, user_update.username): - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail=strings.USERNAME_TAKEN, - ) - - if user_update.email and user_update.email != current_user.email: - if await check_email_is_taken(users_repo, user_update.email): - raise HTTPException( - status_code=HTTP_400_BAD_REQUEST, - detail=strings.EMAIL_TAKEN, - ) - - user = await users_repo.update_user(user=current_user, **user_update.dict()) - - token = jwt.create_access_token_for_user( - user, - str(settings.secret_key.get_secret_value()), - ) - return UserInResponse( - user=UserWithToken( - username=user.username, - email=user.email, - bio=user.bio, - image=user.image, - token=token, - ), - ) diff --git a/.framework/python/backend/app/core/__init__.py b/.framework/python/backend/app/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/core/config.py b/.framework/python/backend/app/core/config.py deleted file mode 100644 index 87f58a8..0000000 --- a/.framework/python/backend/app/core/config.py +++ /dev/null @@ -1,21 +0,0 @@ -from functools import lru_cache -from typing import Dict, Type - -from app.core.settings.app import AppSettings -from app.core.settings.base import AppEnvTypes, BaseAppSettings -from app.core.settings.development import DevAppSettings -from app.core.settings.production import ProdAppSettings -from app.core.settings.test import TestAppSettings - -environments: Dict[AppEnvTypes, Type[AppSettings]] = { - AppEnvTypes.dev: DevAppSettings, - AppEnvTypes.prod: ProdAppSettings, - AppEnvTypes.test: TestAppSettings, -} - - -@lru_cache -def get_app_settings() -> AppSettings: - app_env = BaseAppSettings().app_env - config = environments[app_env] - return config() diff --git a/.framework/python/backend/app/core/events.py b/.framework/python/backend/app/core/events.py deleted file mode 100644 index 3e82ee3..0000000 --- a/.framework/python/backend/app/core/events.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Callable - -from fastapi import FastAPI -from loguru import logger - -from app.core.settings.app import AppSettings -from app.db.events import close_db_connection, connect_to_db - - -def create_start_app_handler( - app: FastAPI, - settings: AppSettings, -) -> Callable: # type: ignore - async def start_app() -> None: - await connect_to_db(app, settings) - - return start_app - - -def create_stop_app_handler(app: FastAPI) -> Callable: # type: ignore - @logger.catch - async def stop_app() -> None: - await close_db_connection(app) - - return stop_app diff --git a/.framework/python/backend/app/core/logging.py b/.framework/python/backend/app/core/logging.py deleted file mode 100644 index 10ceda7..0000000 --- a/.framework/python/backend/app/core/logging.py +++ /dev/null @@ -1,25 +0,0 @@ -import logging -from types import FrameType -from typing import cast - -from loguru import logger - - -class InterceptHandler(logging.Handler): - def emit(self, record: logging.LogRecord) -> None: # pragma: no cover - # Get corresponding Loguru level if it exists - try: - level = logger.level(record.levelname).name - except ValueError: - level = str(record.levelno) - - # Find caller from where originated the logged message - frame, depth = logging.currentframe(), 2 - while frame.f_code.co_filename == logging.__file__: # noqa: WPS609 - frame = cast(FrameType, frame.f_back) - depth += 1 - - logger.opt(depth=depth, exception=record.exc_info).log( - level, - record.getMessage(), - ) diff --git a/.framework/python/backend/app/core/settings/__init__.py b/.framework/python/backend/app/core/settings/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/core/settings/app.py b/.framework/python/backend/app/core/settings/app.py deleted file mode 100644 index 8320500..0000000 --- a/.framework/python/backend/app/core/settings/app.py +++ /dev/null @@ -1,57 +0,0 @@ -import logging -import sys -from typing import Any, Dict, List, Tuple - -from loguru import logger -from pydantic import PostgresDsn, SecretStr - -from app.core.logging import InterceptHandler -from app.core.settings.base import BaseAppSettings - - -class AppSettings(BaseAppSettings): - debug: bool = False - docs_url: str = "/docs" - openapi_prefix: str = "" - openapi_url: str = "/openapi.json" - redoc_url: str = "/redoc" - title: str = "FastAPI example application" - version: str = "0.0.0" - - database_url: PostgresDsn - max_connection_count: int = 5 - min_connection_count: int = 5 - - secret_key: SecretStr = SecretStr("e6F9KvSDf4dyXj") - - api_prefix: str = "/api" - - jwt_token_prefix: str = "Token" - - allowed_hosts: List[str] = ["*"] - - logging_level: int = logging.INFO - loggers: Tuple[str, str] = ("uvicorn.asgi", "uvicorn.access") - - class Config: - validate_assignment = True - - @property - def fastapi_kwargs(self) -> Dict[str, Any]: - return { - "debug": self.debug, - "docs_url": self.docs_url, - "openapi_prefix": self.openapi_prefix, - "openapi_url": self.openapi_url, - "redoc_url": self.redoc_url, - "title": self.title, - "version": self.version, - } - - def configure_logging(self) -> None: - logging.getLogger().handlers = [InterceptHandler()] - for logger_name in self.loggers: - logging_logger = logging.getLogger(logger_name) - logging_logger.handlers = [InterceptHandler(level=self.logging_level)] - - logger.configure(handlers=[{"sink": sys.stderr, "level": self.logging_level}]) diff --git a/.framework/python/backend/app/core/settings/base.py b/.framework/python/backend/app/core/settings/base.py deleted file mode 100644 index 0397cbb..0000000 --- a/.framework/python/backend/app/core/settings/base.py +++ /dev/null @@ -1,16 +0,0 @@ -from enum import Enum - -from pydantic import BaseSettings - - -class AppEnvTypes(Enum): - prod: str = "prod" - dev: str = "dev" - test: str = "test" - - -class BaseAppSettings(BaseSettings): - app_env: AppEnvTypes = AppEnvTypes.prod - - class Config: - env_file = ".env" diff --git a/.framework/python/backend/app/core/settings/development.py b/.framework/python/backend/app/core/settings/development.py deleted file mode 100644 index 041a77d..0000000 --- a/.framework/python/backend/app/core/settings/development.py +++ /dev/null @@ -1,14 +0,0 @@ -import logging - -from app.core.settings.app import AppSettings - - -class DevAppSettings(AppSettings): - debug: bool = True - - title: str = "Dev FastAPI example application" - - logging_level: int = logging.DEBUG - - class Config(AppSettings.Config): - env_file = ".env" diff --git a/.framework/python/backend/app/core/settings/production.py b/.framework/python/backend/app/core/settings/production.py deleted file mode 100644 index f2d3eab..0000000 --- a/.framework/python/backend/app/core/settings/production.py +++ /dev/null @@ -1,6 +0,0 @@ -from app.core.settings.app import AppSettings - - -class ProdAppSettings(AppSettings): - class Config(AppSettings.Config): - env_file = "prod.env" diff --git a/.framework/python/backend/app/core/settings/test.py b/.framework/python/backend/app/core/settings/test.py deleted file mode 100644 index 2b9b15c..0000000 --- a/.framework/python/backend/app/core/settings/test.py +++ /dev/null @@ -1,19 +0,0 @@ -import logging - -from pydantic import PostgresDsn, SecretStr - -from app.core.settings.app import AppSettings - - -class TestAppSettings(AppSettings): - debug: bool = True - - title: str = "Test FastAPI example application" - - secret_key: SecretStr = SecretStr("e6F9KvSDf4dyXj") - - database_url: PostgresDsn - max_connection_count: int = 5 - min_connection_count: int = 5 - - logging_level: int = logging.DEBUG diff --git a/.framework/python/backend/app/db/__init__.py b/.framework/python/backend/app/db/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/db/errors.py b/.framework/python/backend/app/db/errors.py deleted file mode 100644 index bb3ef66..0000000 --- a/.framework/python/backend/app/db/errors.py +++ /dev/null @@ -1,2 +0,0 @@ -class EntityDoesNotExist(Exception): - """Raised when entity was not found in database.""" diff --git a/.framework/python/backend/app/db/events.py b/.framework/python/backend/app/db/events.py deleted file mode 100644 index 5c42303..0000000 --- a/.framework/python/backend/app/db/events.py +++ /dev/null @@ -1,29 +0,0 @@ -import asyncpg -from fastapi import FastAPI -from loguru import logger - -from app.core.settings.app import AppSettings - - -async def connect_to_db(app: FastAPI, settings: AppSettings) -> None: - logger.info("Connecting to PostgreSQL") - - # SQLAlchemy >= 1.4 deprecated the use of `postgres://` in favor of `postgresql://` - # for the database connection url - database_url = settings.database_url.replace("postgres://", "postgresql://") - - app.state.pool = await asyncpg.create_pool( - str(database_url), - min_size=settings.min_connection_count, - max_size=settings.max_connection_count, - ) - - logger.info("Connection established") - - -async def close_db_connection(app: FastAPI) -> None: - logger.info("Closing connection to database") - - await app.state.pool.close() - - logger.info("Connection closed") diff --git a/.framework/python/backend/app/db/migrations/env.py b/.framework/python/backend/app/db/migrations/env.py deleted file mode 100644 index 93122b2..0000000 --- a/.framework/python/backend/app/db/migrations/env.py +++ /dev/null @@ -1,41 +0,0 @@ -import pathlib -import sys -from logging.config import fileConfig - -from alembic import context -from sqlalchemy import engine_from_config, pool - -sys.path.append(str(pathlib.Path(__file__).resolve().parents[3])) - -from app.core.config import get_app_settings # isort:skip - -SETTINGS = get_app_settings() - -# SQLAlchemy >= 1.4 deprecated the use of `postgres://` in favor of `postgresql://` -# for the database connection url -DATABASE_URL = SETTINGS.database_url.replace("postgres://", "postgresql://") - -config = context.config - -fileConfig(config.config_file_name) # type: ignore - -target_metadata = None - -config.set_main_option("sqlalchemy.url", str(DATABASE_URL)) - - -def run_migrations_online() -> None: - connectable = engine_from_config( - config.get_section(config.config_ini_section), - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) - - with connectable.connect() as connection: - context.configure(connection=connection, target_metadata=target_metadata) - - with context.begin_transaction(): - context.run_migrations() - - -run_migrations_online() diff --git a/.framework/python/backend/app/db/migrations/script.py.mako b/.framework/python/backend/app/db/migrations/script.py.mako deleted file mode 100644 index 3217cf0..0000000 --- a/.framework/python/backend/app/db/migrations/script.py.mako +++ /dev/null @@ -1,23 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision | comma,n} -Create Date: ${create_date} - -""" -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} -branch_labels = ${repr(branch_labels)} -depends_on = ${repr(depends_on)} - - -def upgrade() -> None: - ${upgrades if upgrades else "pass"} - - -def downgrade() -> None: - ${downgrades if downgrades else "pass"} diff --git a/.framework/python/backend/app/db/migrations/versions/fdf8821871d7_main_tables.py b/.framework/python/backend/app/db/migrations/versions/fdf8821871d7_main_tables.py deleted file mode 100644 index 04f1036..0000000 --- a/.framework/python/backend/app/db/migrations/versions/fdf8821871d7_main_tables.py +++ /dev/null @@ -1,217 +0,0 @@ -"""main tables - -Revision ID: fdf8821871d7 -Revises: -Create Date: 2019-09-22 01:36:44.791880 - -""" -from typing import Tuple - -import sqlalchemy as sa -from alembic import op -from sqlalchemy import func - -revision = "fdf8821871d7" -down_revision = None -branch_labels = None -depends_on = None - - -def create_updated_at_trigger() -> None: - op.execute( - """ - CREATE FUNCTION update_updated_at_column() - RETURNS TRIGGER AS - $$ - BEGIN - NEW.updated_at = now(); - RETURN NEW; - END; - $$ language 'plpgsql'; - """ - ) - - -def timestamps() -> Tuple[sa.Column, sa.Column]: - return ( - sa.Column( - "created_at", - sa.TIMESTAMP(timezone=True), - nullable=False, - server_default=func.now(), - ), - sa.Column( - "updated_at", - sa.TIMESTAMP(timezone=True), - nullable=False, - server_default=func.now(), - onupdate=func.current_timestamp(), - ), - ) - - -def create_users_table() -> None: - op.create_table( - "users", - sa.Column("id", sa.Integer, primary_key=True), - sa.Column("username", sa.Text, unique=True, nullable=False, index=True), - sa.Column("email", sa.Text, unique=True, nullable=False, index=True), - sa.Column("salt", sa.Text, nullable=False), - sa.Column("hashed_password", sa.Text), - sa.Column("bio", sa.Text, nullable=False, server_default=""), - sa.Column("image", sa.Text), - *timestamps(), - ) - op.execute( - """ - CREATE TRIGGER update_user_modtime - BEFORE UPDATE - ON users - FOR EACH ROW - EXECUTE PROCEDURE update_updated_at_column(); - """ - ) - - -def create_followers_to_followings_table() -> None: - op.create_table( - "followers_to_followings", - sa.Column( - "follower_id", - sa.Integer, - sa.ForeignKey("users.id", ondelete="CASCADE"), - nullable=False, - ), - sa.Column( - "following_id", - sa.Integer, - sa.ForeignKey("users.id", ondelete="CASCADE"), - nullable=False, - ), - ) - op.create_primary_key( - "pk_followers_to_followings", - "followers_to_followings", - ["follower_id", "following_id"], - ) - - -def create_items_table() -> None: - op.create_table( - "items", - sa.Column("id", sa.Integer, primary_key=True), - sa.Column("slug", sa.Text, unique=True, nullable=False, index=True), - sa.Column("title", sa.Text, nullable=False), - sa.Column("description", sa.Text, nullable=False), - sa.Column("body", sa.Text, nullable=True), - sa.Column("image", sa.Text, nullable=True), - sa.Column( - "seller_id", sa.Integer, sa.ForeignKey("users.id", ondelete="SET NULL") - ), - *timestamps(), - ) - op.execute( - """ - CREATE TRIGGER update_item_modtime - BEFORE UPDATE - ON items - FOR EACH ROW - EXECUTE PROCEDURE update_updated_at_column(); - """ - ) - - -def create_tags_table() -> None: - op.create_table("tags", sa.Column("tag", sa.Text, primary_key=True)) - - -def create_items_to_tags_table() -> None: - op.create_table( - "items_to_tags", - sa.Column( - "item_id", - sa.Integer, - sa.ForeignKey("items.id", ondelete="CASCADE"), - nullable=False, - ), - sa.Column( - "tag", - sa.Text, - sa.ForeignKey("tags.tag", ondelete="CASCADE"), - nullable=False, - ), - ) - op.create_primary_key( - "pk_items_to_tags", "items_to_tags", ["item_id", "tag"] - ) - - -def create_favorites_table() -> None: - op.create_table( - "favorites", - sa.Column( - "user_id", - sa.Integer, - sa.ForeignKey("users.id", ondelete="CASCADE"), - nullable=False, - ), - sa.Column( - "item_id", - sa.Integer, - sa.ForeignKey("items.id", ondelete="CASCADE"), - nullable=False, - ), - ) - op.create_primary_key("pk_favorites", "favorites", ["user_id", "item_id"]) - - -def create_comments_table() -> None: - op.create_table( - "comments", - sa.Column("id", sa.Integer, primary_key=True), - sa.Column("body", sa.Text, nullable=False), - sa.Column( - "seller_id", - sa.Integer, - sa.ForeignKey("users.id", ondelete="CASCADE"), - nullable=False, - ), - sa.Column( - "item_id", - sa.Integer, - sa.ForeignKey("items.id", ondelete="CASCADE"), - nullable=False, - ), - *timestamps(), - ) - op.execute( - """ - CREATE TRIGGER update_comment_modtime - BEFORE UPDATE - ON comments - FOR EACH ROW - EXECUTE PROCEDURE update_updated_at_column(); - """ - ) - - -def upgrade() -> None: - create_updated_at_trigger() - create_users_table() - create_followers_to_followings_table() - create_items_table() - create_tags_table() - create_items_to_tags_table() - create_favorites_table() - create_comments_table() - - -def downgrade() -> None: - op.drop_table("comments") - op.drop_table("favorites") - op.drop_table("items_to_tags") - op.drop_table("tags") - op.drop_table("items") - op.drop_table("followers_to_followings") - op.drop_table("users") - op.execute("DROP FUNCTION update_updated_at_column") diff --git a/.framework/python/backend/app/db/queries/__init__.py b/.framework/python/backend/app/db/queries/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/db/queries/queries.py b/.framework/python/backend/app/db/queries/queries.py deleted file mode 100644 index a190595..0000000 --- a/.framework/python/backend/app/db/queries/queries.py +++ /dev/null @@ -1,5 +0,0 @@ -import pathlib - -import aiosql - -queries = aiosql.from_path(pathlib.Path(__file__).parent / "sql", "asyncpg") diff --git a/.framework/python/backend/app/db/queries/queries.pyi b/.framework/python/backend/app/db/queries/queries.pyi deleted file mode 100644 index 5f20e85..0000000 --- a/.framework/python/backend/app/db/queries/queries.pyi +++ /dev/null @@ -1,125 +0,0 @@ -"""Typings for queries generated by aiosql""" - -from typing import Dict, Optional, Sequence - -from asyncpg import Connection, Record - -class TagsQueriesMixin: - async def get_all_tags(self, conn: Connection) -> Record: ... - async def create_new_tags( - self, conn: Connection, tags: Sequence[Dict[str, str]] - ) -> None: ... - -class UsersQueriesMixin: - async def get_user_by_email(self, conn: Connection, *, email: str) -> Record: ... - async def get_user_by_username( - self, conn: Connection, *, username: str - ) -> Record: ... - async def create_new_user( - self, - conn: Connection, - *, - username: str, - email: str, - salt: str, - hashed_password: str - ) -> Record: ... - async def update_user_by_username( - self, - conn: Connection, - *, - username: str, - new_username: str, - new_email: str, - new_salt: str, - new_password: str, - new_bio: Optional[str], - new_image: Optional[str] - ) -> Record: ... - -class ProfilesQueriesMixin: - async def is_user_following_for_another( - self, conn: Connection, *, follower_username: str, following_username: str - ) -> Record: ... - async def subscribe_user_to_another( - self, conn: Connection, *, follower_username: str, following_username: str - ) -> None: ... - async def unsubscribe_user_from_another( - self, conn: Connection, *, follower_username: str, following_username: str - ) -> None: ... - -class CommentsQueriesMixin: - async def get_comments_for_item_by_slug( - self, conn: Connection, *, slug: str - ) -> Record: ... - async def get_comment_by_id_and_slug( - self, conn: Connection, *, comment_id: int, item_slug: str - ) -> Record: ... - async def create_new_comment( - self, conn: Connection, *, body: str, item_slug: str, seller_username: str - ) -> Record: ... - async def delete_comment_by_id( - self, conn: Connection, *, comment_id: int, seller_username: str - ) -> None: ... - -class ItemsQueriesMixin: - async def add_item_to_favorites( - self, conn: Connection, *, username: str, slug: str - ) -> None: ... - async def remove_item_from_favorites( - self, conn: Connection, *, username: str, slug: str - ) -> None: ... - async def is_item_in_favorites( - self, conn: Connection, *, username: str, slug: str - ) -> Record: ... - async def get_favorites_count_for_item( - self, conn: Connection, *, slug: str - ) -> Record: ... - async def get_tags_for_item_by_slug( - self, conn: Connection, *, slug: str - ) -> Record: ... - async def get_item_by_slug(self, conn: Connection, *, slug: str) -> Record: ... - async def create_new_item( - self, - conn: Connection, - *, - slug: str, - title: str, - description: str, - body: str, - seller_username: str, - image: str - ) -> Record: ... - async def add_tags_to_item( - self, conn: Connection, tags_slugs: Sequence[Dict[str, str]] - ) -> None: ... - async def delete_tags_from_item( - self, conn: Connection, *, slug: str - ) -> None: ... - async def update_item( - self, - conn: Connection, - *, - slug: str, - seller_username: str, - new_title: str, - new_body: str, - new_description: str, - new_image: str - ) -> Record: ... - async def delete_item( - self, conn: Connection, *, slug: str, seller_username: str - ) -> None: ... - async def get_items_for_feed( - self, conn: Connection, *, follower_username: str, limit: int, offset: int - ) -> Record: ... - -class Queries( - TagsQueriesMixin, - UsersQueriesMixin, - ProfilesQueriesMixin, - CommentsQueriesMixin, - ItemsQueriesMixin, -): ... - -queries: Queries diff --git a/.framework/python/backend/app/db/queries/sql/comments.sql b/.framework/python/backend/app/db/queries/sql/comments.sql deleted file mode 100644 index 7a342ec..0000000 --- a/.framework/python/backend/app/db/queries/sql/comments.sql +++ /dev/null @@ -1,41 +0,0 @@ --- name: get-comments-for-item-by-slug -SELECT c.id, - c.body, - c.created_at, - c.updated_at, - (SELECT username FROM users WHERE id = c.seller_id) as seller_username -FROM comments c - INNER JOIN items a ON c.item_id = a.id AND (a.slug = :slug) -ORDER BY c.created_at DESC; - --- name: get-comment-by-id-and-slug^ -SELECT c.id, - c.body, - c.created_at, - c.updated_at, - (SELECT username FROM users WHERE id = c.seller_id) as seller_username -FROM comments c - INNER JOIN items a ON c.item_id = a.id AND (a.slug = :item_slug) -WHERE c.id = :comment_id; - --- name: create-new-comment 0 THEN TRUE ELSE FALSE END AS favorited -FROM favorites -WHERE user_id = (SELECT id FROM users WHERE username = :username) - AND item_id = (SELECT id FROM items WHERE slug = :slug); - - --- name: get-favorites-count-for-item^ -SELECT count(*) as favorites_count -FROM favorites -WHERE item_id = (SELECT id FROM items WHERE slug = :slug); - - --- name: get-tags-for-item-by-slug -SELECT t.tag -FROM tags t - INNER JOIN items_to_tags att ON - t.tag = att.tag - AND - att.item_id = (SELECT id FROM items WHERE slug = :slug); - - --- name: get-item-by-slug^ -SELECT id, - slug, - title, - description, - body, - image, - created_at, - updated_at, - (SELECT username FROM users WHERE id = seller_id) AS seller_username -FROM items -WHERE slug = :slug -LIMIT 1; - - --- name: create-new-item None: - super().__init__("${0}".format(count)) - - -class TypedTable(Table): - __table__ = "" - - def __init__( - self, - name: Optional[str] = None, - schema: Optional[str] = None, - alias: Optional[str] = None, - query_cls: Optional[Query] = None, - ) -> None: - if name is None: - if self.__table__: - name = self.__table__ - else: - name = self.__class__.__name__ - - super().__init__(name, schema, alias, query_cls) - - -class Users(TypedTable): - __table__ = "users" - - id: int - username: str - - -class Items(TypedTable): - __table__ = "items" - - id: int - slug: str - title: str - description: str - body: str - seller_id: int - created_at: datetime - updated_at: datetime - - -class Tags(TypedTable): - __table__ = "tags" - - tag: str - - -class ItemsToTags(TypedTable): - __table__ = "items_to_tags" - - item_id: int - tag: str - - -class Favorites(TypedTable): - __table__ = "favorites" - - item_id: int - user_id: int - - -users = Users() -items = Items() -tags = Tags() -items_to_tags = ItemsToTags() -favorites = Favorites() diff --git a/.framework/python/backend/app/db/repositories/__init__.py b/.framework/python/backend/app/db/repositories/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/db/repositories/base.py b/.framework/python/backend/app/db/repositories/base.py deleted file mode 100644 index 8f8a5c3..0000000 --- a/.framework/python/backend/app/db/repositories/base.py +++ /dev/null @@ -1,10 +0,0 @@ -from asyncpg.connection import Connection - - -class BaseRepository: - def __init__(self, conn: Connection) -> None: - self._conn = conn - - @property - def connection(self) -> Connection: - return self._conn diff --git a/.framework/python/backend/app/db/repositories/comments.py b/.framework/python/backend/app/db/repositories/comments.py deleted file mode 100644 index cb432b3..0000000 --- a/.framework/python/backend/app/db/repositories/comments.py +++ /dev/null @@ -1,103 +0,0 @@ -from typing import List, Optional - -from asyncpg import Connection, Record - -from app.db.errors import EntityDoesNotExist -from app.db.queries.queries import queries -from app.db.repositories.base import BaseRepository -from app.db.repositories.profiles import ProfilesRepository -from app.models.domain.items import Item -from app.models.domain.comments import Comment -from app.models.domain.users import User - - -class CommentsRepository(BaseRepository): - def __init__(self, conn: Connection) -> None: - super().__init__(conn) - self._profiles_repo = ProfilesRepository(conn) - - async def get_comment_by_id( - self, - *, - comment_id: int, - item: Item, - user: Optional[User] = None, - ) -> Comment: - comment_row = await queries.get_comment_by_id_and_slug( - self.connection, - comment_id=comment_id, - item_slug=item.slug, - ) - if comment_row: - return await self._get_comment_from_db_record( - comment_row=comment_row, - seller_username=comment_row["seller_username"], - requested_user=user, - ) - - raise EntityDoesNotExist( - "comment with id {0} does not exist".format(comment_id), - ) - - async def get_comments_for_item( - self, - *, - item: Item, - user: Optional[User] = None, - ) -> List[Comment]: - comments_rows = await queries.get_comments_for_item_by_slug( - self.connection, - slug=item.slug, - ) - return [ - await self._get_comment_from_db_record( - comment_row=comment_row, - seller_username=comment_row["seller_username"], - requested_user=user, - ) - for comment_row in comments_rows - ] - - async def create_comment_for_item( - self, - *, - body: str, - item: Item, - user: User, - ) -> Comment: - comment_row = await queries.create_new_comment( - self.connection, - body=body, - item_slug=item.slug, - seller_username=user.username, - ) - return await self._get_comment_from_db_record( - comment_row=comment_row, - seller_username=comment_row["seller_username"], - requested_user=user, - ) - - async def delete_comment(self, *, comment: Comment) -> None: - await queries.delete_comment_by_id( - self.connection, - comment_id=comment.id_, - seller_username=comment.seller.username, - ) - - async def _get_comment_from_db_record( - self, - *, - comment_row: Record, - seller_username: str, - requested_user: Optional[User], - ) -> Comment: - return Comment( - id_=comment_row["id"], - body=comment_row["body"], - seller=await self._profiles_repo.get_profile_by_username( - username=seller_username, - requested_user=requested_user, - ), - created_at=comment_row["created_at"], - updated_at=comment_row["updated_at"], - ) diff --git a/.framework/python/backend/app/db/repositories/items.py b/.framework/python/backend/app/db/repositories/items.py deleted file mode 100644 index 8ff146a..0000000 --- a/.framework/python/backend/app/db/repositories/items.py +++ /dev/null @@ -1,353 +0,0 @@ -from typing import List, Optional, Sequence, Union - -from asyncpg import Connection, Record -from pypika import Query, Order - -from app.db.errors import EntityDoesNotExist -from app.db.queries.queries import queries -from app.db.queries.tables import ( - Parameter, - items, - items_to_tags, - favorites, - tags as tags_table, - users, -) -from app.db.repositories.base import BaseRepository -from app.db.repositories.profiles import ProfilesRepository -from app.db.repositories.tags import TagsRepository -from app.models.domain.items import Item -from app.models.domain.users import User - -SELLER_USERNAME_ALIAS = "seller_username" -SLUG_ALIAS = "slug" - -CAMEL_OR_SNAKE_CASE_TO_WORDS = r"^[a-z\d_\-]+|[A-Z\d_\-][^A-Z\d_\-]*" - - -class ItemsRepository(BaseRepository): # noqa: WPS214 - def __init__(self, conn: Connection) -> None: - super().__init__(conn) - self._profiles_repo = ProfilesRepository(conn) - self._tags_repo = TagsRepository(conn) - - async def create_item( # noqa: WPS211 - self, - *, - slug: str, - title: str, - description: str, - seller: User, - body: Optional[str] = None, - image: Optional[str] = None, - tags: Optional[Sequence[str]] = None, - ) -> Item: - async with self.connection.transaction(): - item_row = await queries.create_new_item( - self.connection, - slug=slug, - title=title, - description=description, - body=body, - seller_username=seller.username, - image=image - ) - - if tags: - await self._tags_repo.create_tags_that_dont_exist(tags=tags) - await self._link_item_with_tags(slug=slug, tags=tags) - - return await self._get_item_from_db_record( - item_row=item_row, - slug=slug, - seller_username=item_row[SELLER_USERNAME_ALIAS], - requested_user=seller, - ) - - async def update_item( # noqa: WPS211 - self, - *, - item: Item, - slug: Optional[str] = None, - title: Optional[str] = None, - body: Optional[str] = None, - description: Optional[str] = None, - image: Optional[str] = None, - tags: Optional[Sequence[str]] = None, - ) -> Item: - updated_item = item.copy(deep=True) - updated_item.title = title or item.title - updated_item.body = body or item.body - updated_item.description = description or item.description - updated_item.image = image or item.image - - async with self.connection.transaction(): - updated_item.updated_at = await queries.update_item( - self.connection, - slug=item.slug, - seller_username=item.seller.username, - new_title=updated_item.title, - new_body=updated_item.body, - new_description=updated_item.description, - new_image=updated_item.image, - ) - - if tags: - await self._tags_repo.create_tags_that_dont_exist(tags=tags) - await self._unlink_item_from_tags(slug=item.slug) - await self._link_item_with_tags(slug=item.slug, tags=tags) - - return await self.get_item_by_slug( - slug=item.slug, - requested_user=item.seller, - ) - - async def delete_item(self, *, item: Item) -> None: - async with self.connection.transaction(): - await queries.delete_item( - self.connection, - slug=item.slug, - seller_username=item.seller.username, - ) - - async def filter_items( # noqa: WPS211 - self, - *, - tag: Optional[str] = None, - seller: Optional[str] = None, - favorited: Optional[str] = None, - limit: int = 20, - offset: int = 0, - requested_user: Optional[User] = None, - ) -> List[Item]: - query_params: List[Union[str, int]] = [] - query_params_count = 0 - - # fmt: off - query = Query.from_( - items, - ).select( - items.id, - items.slug, - items.title, - items.description, - items.body, - items.image, - items.created_at, - items.updated_at, - Query.from_( - users, - ).where( - users.id == items.seller_id, - ).select( - users.username, - ).as_( - SELLER_USERNAME_ALIAS, - ), - ).orderby( - items.created_at, order=Order.desc, - ) - # fmt: on - - if tag: - query_params.append(tag) - query_params_count += 1 - - # fmt: off - query = query.join( - items_to_tags, - ).on( - (items.id == items_to_tags.item_id) & ( - items_to_tags.tag == Query.from_( - tags_table, - ).where( - tags_table.tag == Parameter(query_params_count), - ).select( - tags_table.tag, - ) - ), - ) - # fmt: on - - if seller: - query_params.append(seller) - query_params_count += 1 - - # fmt: off - query = query.join( - users, - ).on( - (items.seller_id == users.id) & ( - users.id == Query.from_( - users, - ).where( - users.username == Parameter(query_params_count), - ).select( - users.id, - ) - ), - ) - # fmt: on - - if favorited: - query_params.append(favorited) - query_params_count += 1 - - # fmt: off - query = query.join( - favorites, - ).on( - (items.id == favorites.item_id) & ( - favorites.user_id == Query.from_( - users, - ).where( - users.username == Parameter(query_params_count), - ).select( - users.id, - ) - ), - ) - # fmt: on - - query = query.limit(Parameter(query_params_count + 1)).offset( - Parameter(query_params_count + 2), - ) - query_params.extend([limit, offset]) - - items_rows = await self.connection.fetch(query.get_sql(), *query_params) - - return [ - await self.get_item_by_slug(slug=item_row['slug'], requested_user=requested_user) - for item_row in items_rows - ] - - async def get_items_for_user_feed( - self, - *, - user: User, - limit: int = 20, - offset: int = 0, - ) -> List[Item]: - items_rows = await queries.get_items_for_feed( - self.connection, - follower_username=user.username, - limit=limit, - offset=offset, - ) - return [ - await self._get_item_from_db_record( - item_row=item_row, - slug=item_row[SLUG_ALIAS], - seller_username=item_row[SELLER_USERNAME_ALIAS], - requested_user=user, - ) - for item_row in items_rows - ] - - async def get_item_by_slug( - self, - *, - slug: str, - requested_user: Optional[User] = None, - ) -> Item: - item_row = await queries.get_item_by_slug(self.connection, slug=slug) - if item_row: - return await self._get_item_from_db_record( - item_row=item_row, - slug=item_row[SLUG_ALIAS], - seller_username=item_row[SELLER_USERNAME_ALIAS], - requested_user=requested_user, - ) - - raise EntityDoesNotExist("item with slug {0} does not exist".format(slug)) - - async def get_tags_for_item_by_slug(self, *, slug: str) -> List[str]: - tag_rows = await queries.get_tags_for_item_by_slug( - self.connection, - slug=slug, - ) - return [row["tag"] for row in tag_rows] - - async def get_favorites_count_for_item_by_slug(self, *, slug: str) -> int: - return ( - await queries.get_favorites_count_for_item(self.connection, slug=slug) - )["favorites_count"] - - async def is_item_favorited_by_user(self, *, slug: str, user: User) -> bool: - return ( - await queries.is_item_in_favorites( - self.connection, - username=user.username, - slug=slug, - ) - )["favorited"] - - async def add_item_into_favorites(self, *, item: Item, user: User) -> None: - await queries.add_item_to_favorites( - self.connection, - username=user.username, - slug=item.slug, - ) - - async def remove_item_from_favorites( - self, - *, - item: Item, - user: User, - ) -> None: - await queries.remove_item_from_favorites( - self.connection, - username=user.username, - slug=item.slug, - ) - - async def _get_item_from_db_record( - self, - *, - item_row: Record, - slug: str, - seller_username: str, - requested_user: Optional[User], - ) -> Item: - title_query = Query.from_(items).select(items.title).where(items.slug == slug) - result_rows = await self.connection.fetch(title_query.get_sql()) - if not len(result_rows): - raise Exception(f'No item with slug {slug}') - title = result_rows[0]['title'] - - return Item( - id_=item_row["id"], - slug=slug, - title=title, - description=item_row["description"], - body=item_row["body"], - image=item_row["image"], - seller=await self._profiles_repo.get_profile_by_username( - username=seller_username, - requested_user=requested_user, - ), - tags=await self.get_tags_for_item_by_slug(slug=slug), - favorites_count=await self.get_favorites_count_for_item_by_slug( - slug=slug, - ), - favorited=await self.is_item_favorited_by_user( - slug=slug, - user=requested_user, - ) - if requested_user - else False, - created_at=item_row["created_at"], - updated_at=item_row["updated_at"], - ) - - async def _link_item_with_tags(self, *, slug: str, tags: Sequence[str]) -> None: - await queries.add_tags_to_item( - self.connection, - [{SLUG_ALIAS: slug, "tag": tag} for tag in tags], - ) - - async def _unlink_item_with_tags(self, *, slug: str) -> None: - await queries.delete_tags_from_item( - self.connection, - slug=slug, - ) diff --git a/.framework/python/backend/app/db/repositories/profiles.py b/.framework/python/backend/app/db/repositories/profiles.py deleted file mode 100644 index 20d43a0..0000000 --- a/.framework/python/backend/app/db/repositories/profiles.py +++ /dev/null @@ -1,74 +0,0 @@ -from typing import Optional, Union - -from asyncpg import Connection - -from app.db.queries.queries import queries -from app.db.repositories.base import BaseRepository -from app.db.repositories.users import UsersRepository -from app.models.domain.profiles import Profile -from app.models.domain.users import User - -UserLike = Union[User, Profile] - - -class ProfilesRepository(BaseRepository): - def __init__(self, conn: Connection): - super().__init__(conn) - self._users_repo = UsersRepository(conn) - - async def get_profile_by_username( - self, - *, - username: str, - requested_user: Optional[UserLike], - ) -> Profile: - user = await self._users_repo.get_user_by_username(username=username) - - profile = Profile(username=user.username, bio=user.bio, image=user.image) - if requested_user: - profile.following = await self.is_user_following_for_another_user( - target_user=user, - requested_user=requested_user, - ) - - return profile - - async def is_user_following_for_another_user( - self, - *, - target_user: UserLike, - requested_user: UserLike, - ) -> bool: - return ( - await queries.is_user_following_for_another( - self.connection, - follower_username=requested_user.username, - following_username=target_user.username, - ) - )["is_following"] - - async def add_user_into_followers( - self, - *, - target_user: UserLike, - requested_user: UserLike, - ) -> None: - async with self.connection.transaction(): - await queries.subscribe_user_to_another( - self.connection, - follower_username=requested_user.username, - following_username=target_user.username, - ) - - async def remove_user_from_followers( - self, - *, - target_user: UserLike, - requested_user: UserLike, - ) -> None: - async with self.connection.transaction(): - await queries.unsubscribe_user_from_another( - self.connection, - follower_username=requested_user.username, - following_username=target_user.username, - ) diff --git a/.framework/python/backend/app/db/repositories/tags.py b/.framework/python/backend/app/db/repositories/tags.py deleted file mode 100644 index 5734992..0000000 --- a/.framework/python/backend/app/db/repositories/tags.py +++ /dev/null @@ -1,13 +0,0 @@ -from typing import List, Sequence - -from app.db.queries.queries import queries -from app.db.repositories.base import BaseRepository - - -class TagsRepository(BaseRepository): - async def get_all_tags(self) -> List[str]: - tags_row = await queries.get_all_tags(self.connection) - return [tag[0] for tag in tags_row] - - async def create_tags_that_dont_exist(self, *, tags: Sequence[str]) -> None: - await queries.create_new_tags(self.connection, [{"tag": tag} for tag in tags]) diff --git a/.framework/python/backend/app/db/repositories/users.py b/.framework/python/backend/app/db/repositories/users.py deleted file mode 100644 index 0bb18ec..0000000 --- a/.framework/python/backend/app/db/repositories/users.py +++ /dev/null @@ -1,81 +0,0 @@ -from typing import Optional - -from app.db.errors import EntityDoesNotExist -from app.db.queries.queries import queries -from app.db.repositories.base import BaseRepository -from app.models.domain.users import User, UserInDB - - -class UsersRepository(BaseRepository): - async def get_user_by_email(self, *, email: str) -> UserInDB: - user_row = await queries.get_user_by_email(self.connection, email=email) - if user_row: - return UserInDB(**user_row) - - raise EntityDoesNotExist("user with email {0} does not exist".format(email)) - - async def get_user_by_username(self, *, username: str) -> UserInDB: - user_row = await queries.get_user_by_username( - self.connection, - username=username, - ) - if user_row: - return UserInDB(**user_row) - - raise EntityDoesNotExist( - "user with username {0} does not exist".format(username), - ) - - async def create_user( - self, - *, - username: str, - email: str, - password: str, - ) -> UserInDB: - user = UserInDB(username=username, email=email) - user.change_password(password) - - async with self.connection.transaction(): - user_row = await queries.create_new_user( - self.connection, - username=user.username, - email=user.email, - salt=user.salt, - hashed_password=user.hashed_password, - ) - - return user.copy(update=dict(user_row)) - - async def update_user( # noqa: WPS211 - self, - *, - user: User, - username: Optional[str] = None, - email: Optional[str] = None, - password: Optional[str] = None, - bio: Optional[str] = None, - image: Optional[str] = None, - ) -> UserInDB: - user_in_db = await self.get_user_by_username(username=user.username) - - user_in_db.username = username or user_in_db.username - user_in_db.email = email or user_in_db.email - user_in_db.bio = bio or user_in_db.bio - user_in_db.image = image or user_in_db.image - if password: - user_in_db.change_password(password) - - async with self.connection.transaction(): - user_in_db.updated_at = await queries.update_user_by_username( - self.connection, - username=user.username, - new_username=user_in_db.username, - new_email=user_in_db.email, - new_salt=user_in_db.salt, - new_password=user_in_db.hashed_password, - new_bio=user_in_db.bio, - new_image=user_in_db.image, - ) - - return user_in_db diff --git a/.framework/python/backend/app/db/seeds.py b/.framework/python/backend/app/db/seeds.py deleted file mode 100644 index 6509e2d..0000000 --- a/.framework/python/backend/app/db/seeds.py +++ /dev/null @@ -1 +0,0 @@ -print('Please fill the seeds file') diff --git a/.framework/python/backend/app/main.py b/.framework/python/backend/app/main.py deleted file mode 100644 index cff8033..0000000 --- a/.framework/python/backend/app/main.py +++ /dev/null @@ -1,47 +0,0 @@ -from fastapi import FastAPI -from fastapi.exceptions import RequestValidationError -from starlette.exceptions import HTTPException -from starlette.middleware.cors import CORSMiddleware - -from app.api.errors.http_error import http_error_handler -from app.api.errors.validation_error import http422_error_handler -from app.api.routes.api import router as api_router -from app.api.routes.home import router as home_router -from app.core.config import get_app_settings -from app.core.events import create_start_app_handler, create_stop_app_handler - - -def get_application() -> FastAPI: - settings = get_app_settings() - - settings.configure_logging() - - application = FastAPI(**settings.fastapi_kwargs) - - application.add_middleware( - CORSMiddleware, - allow_origins=settings.allowed_hosts, - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], - ) - - application.add_event_handler( - "startup", - create_start_app_handler(application, settings), - ) - application.add_event_handler( - "shutdown", - create_stop_app_handler(application), - ) - - application.add_exception_handler(HTTPException, http_error_handler) - application.add_exception_handler(RequestValidationError, http422_error_handler) - - application.include_router(home_router) - application.include_router(api_router, prefix=settings.api_prefix) - - return application - - -app = get_application() diff --git a/.framework/python/backend/app/models/__init__.py b/.framework/python/backend/app/models/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/models/common.py b/.framework/python/backend/app/models/common.py deleted file mode 100644 index fdc515b..0000000 --- a/.framework/python/backend/app/models/common.py +++ /dev/null @@ -1,19 +0,0 @@ -import datetime - -from pydantic import BaseModel, Field, validator - - -class DateTimeModelMixin(BaseModel): - created_at: datetime.datetime = None # type: ignore - updated_at: datetime.datetime = None # type: ignore - - @validator("created_at", "updated_at", pre=True) - def default_datetime( - cls, # noqa: N805 - value: datetime.datetime, # noqa: WPS110 - ) -> datetime.datetime: - return value or datetime.datetime.now() - - -class IDModelMixin(BaseModel): - id_: int = Field(0, alias="id") diff --git a/.framework/python/backend/app/models/domain/__init__.py b/.framework/python/backend/app/models/domain/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/models/domain/comments.py b/.framework/python/backend/app/models/domain/comments.py deleted file mode 100644 index c5ec749..0000000 --- a/.framework/python/backend/app/models/domain/comments.py +++ /dev/null @@ -1,8 +0,0 @@ -from app.models.common import DateTimeModelMixin, IDModelMixin -from app.models.domain.profiles import Profile -from app.models.domain.rwmodel import RWModel - - -class Comment(IDModelMixin, DateTimeModelMixin, RWModel): - body: str - seller: Profile diff --git a/.framework/python/backend/app/models/domain/items.py b/.framework/python/backend/app/models/domain/items.py deleted file mode 100644 index 3e95353..0000000 --- a/.framework/python/backend/app/models/domain/items.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import List, Optional - -from app.models.common import DateTimeModelMixin, IDModelMixin -from app.models.domain.profiles import Profile -from app.models.domain.rwmodel import RWModel - - -class Item(IDModelMixin, DateTimeModelMixin, RWModel): - slug: str - title: str - description: str - tags: List[str] - seller: Profile - favorited: bool - favorites_count: int - image: Optional[str] - body: Optional[str] diff --git a/.framework/python/backend/app/models/domain/profiles.py b/.framework/python/backend/app/models/domain/profiles.py deleted file mode 100644 index b1e6ac0..0000000 --- a/.framework/python/backend/app/models/domain/profiles.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import Optional - -from app.models.domain.rwmodel import RWModel - - -class Profile(RWModel): - username: str - bio: str = "" - image: Optional[str] = None - following: bool = False diff --git a/.framework/python/backend/app/models/domain/rwmodel.py b/.framework/python/backend/app/models/domain/rwmodel.py deleted file mode 100644 index 1c34f3b..0000000 --- a/.framework/python/backend/app/models/domain/rwmodel.py +++ /dev/null @@ -1,21 +0,0 @@ -import datetime - -from pydantic import BaseConfig, BaseModel - - -def convert_datetime_to_realworld(dt: datetime.datetime) -> str: - return dt.replace(tzinfo=datetime.timezone.utc).isoformat().replace("+00:00", "Z") - - -def convert_field_to_camel_case(string: str) -> str: - return "".join( - word if index == 0 else word.capitalize() - for index, word in enumerate(string.split("_")) - ) - - -class RWModel(BaseModel): - class Config(BaseConfig): - allow_population_by_field_name = True - json_encoders = {datetime.datetime: convert_datetime_to_realworld} - alias_generator = convert_field_to_camel_case diff --git a/.framework/python/backend/app/models/domain/users.py b/.framework/python/backend/app/models/domain/users.py deleted file mode 100644 index 3da2f9d..0000000 --- a/.framework/python/backend/app/models/domain/users.py +++ /dev/null @@ -1,24 +0,0 @@ -from typing import Optional - -from app.models.common import DateTimeModelMixin, IDModelMixin -from app.models.domain.rwmodel import RWModel -from app.services import security - - -class User(RWModel): - username: str - email: str - bio: str = "" - image: Optional[str] = None - - -class UserInDB(IDModelMixin, DateTimeModelMixin, User): - salt: str = "" - hashed_password: str = "" - - def check_password(self, password: str) -> bool: - return security.verify_password(self.salt + password, self.hashed_password) - - def change_password(self, password: str) -> None: - self.salt = security.generate_salt() - self.hashed_password = security.get_password_hash(self.salt + password) diff --git a/.framework/python/backend/app/models/schemas/__init__.py b/.framework/python/backend/app/models/schemas/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/models/schemas/comments.py b/.framework/python/backend/app/models/schemas/comments.py deleted file mode 100644 index e230697..0000000 --- a/.framework/python/backend/app/models/schemas/comments.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import List - -from app.models.domain.comments import Comment -from app.models.schemas.rwschema import RWSchema - - -class ListOfCommentsInResponse(RWSchema): - comments: List[Comment] - - -class CommentInResponse(RWSchema): - comment: Comment - - -class CommentInCreate(RWSchema): - body: str diff --git a/.framework/python/backend/app/models/schemas/items.py b/.framework/python/backend/app/models/schemas/items.py deleted file mode 100644 index 5c43a59..0000000 --- a/.framework/python/backend/app/models/schemas/items.py +++ /dev/null @@ -1,46 +0,0 @@ -from typing import List, Optional - -from pydantic import BaseModel, Field - -from app.models.domain.items import Item -from app.models.schemas.rwschema import RWSchema - -DEFAULT_ITEMS_LIMIT = 20 -DEFAULT_ITEMS_OFFSET = 0 - - -class ItemForResponse(RWSchema, Item): - tags: List[str] = Field(..., alias="tagList") - - -class ItemInResponse(RWSchema): - item: ItemForResponse - - -class ItemInCreate(RWSchema): - title: str - description: str - body: Optional[str] = None - image: Optional[str] = None - tags: List[str] = Field([], alias="tagList") - - -class ItemInUpdate(RWSchema): - title: Optional[str] = None - description: Optional[str] = None - body: Optional[str] = None - image: Optional[str] = None - tags: Optional[List[str]] = Field(None, alias="tagList") - - -class ListOfItemsInResponse(RWSchema): - items: List[ItemForResponse] - items_count: int - - -class ItemsFilters(BaseModel): - tag: Optional[str] = None - seller: Optional[str] = None - favorited: Optional[str] = None - limit: int = Field(DEFAULT_ITEMS_LIMIT, ge=1) - offset: int = Field(DEFAULT_ITEMS_OFFSET, ge=0) diff --git a/.framework/python/backend/app/models/schemas/jwt.py b/.framework/python/backend/app/models/schemas/jwt.py deleted file mode 100644 index 56d1fa3..0000000 --- a/.framework/python/backend/app/models/schemas/jwt.py +++ /dev/null @@ -1,12 +0,0 @@ -from datetime import datetime - -from pydantic import BaseModel - - -class JWTMeta(BaseModel): - exp: datetime - sub: str - - -class JWTUser(BaseModel): - username: str diff --git a/.framework/python/backend/app/models/schemas/profiles.py b/.framework/python/backend/app/models/schemas/profiles.py deleted file mode 100644 index 5662dfc..0000000 --- a/.framework/python/backend/app/models/schemas/profiles.py +++ /dev/null @@ -1,7 +0,0 @@ -from pydantic import BaseModel - -from app.models.domain.profiles import Profile - - -class ProfileInResponse(BaseModel): - profile: Profile diff --git a/.framework/python/backend/app/models/schemas/rwschema.py b/.framework/python/backend/app/models/schemas/rwschema.py deleted file mode 100644 index 018ad4b..0000000 --- a/.framework/python/backend/app/models/schemas/rwschema.py +++ /dev/null @@ -1,6 +0,0 @@ -from app.models.domain.rwmodel import RWModel - - -class RWSchema(RWModel): - class Config(RWModel.Config): - orm_mode = True diff --git a/.framework/python/backend/app/models/schemas/tags.py b/.framework/python/backend/app/models/schemas/tags.py deleted file mode 100644 index e9655fb..0000000 --- a/.framework/python/backend/app/models/schemas/tags.py +++ /dev/null @@ -1,7 +0,0 @@ -from typing import List - -from pydantic import BaseModel - - -class TagsInList(BaseModel): - tags: List[str] diff --git a/.framework/python/backend/app/models/schemas/users.py b/.framework/python/backend/app/models/schemas/users.py deleted file mode 100644 index d0f2bba..0000000 --- a/.framework/python/backend/app/models/schemas/users.py +++ /dev/null @@ -1,31 +0,0 @@ -from typing import Optional - -from pydantic import BaseModel, EmailStr, HttpUrl - -from app.models.domain.users import User -from app.models.schemas.rwschema import RWSchema - - -class UserInLogin(RWSchema): - email: EmailStr - password: str - - -class UserInCreate(UserInLogin): - username: str - - -class UserInUpdate(BaseModel): - username: Optional[str] = None - email: Optional[EmailStr] = None - password: Optional[str] = None - bio: Optional[str] = None - image: Optional[HttpUrl] = None - - -class UserWithToken(User): - token: str - - -class UserInResponse(RWSchema): - user: UserWithToken diff --git a/.framework/python/backend/app/resources/__init__.py b/.framework/python/backend/app/resources/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/resources/strings.py b/.framework/python/backend/app/resources/strings.py deleted file mode 100644 index d7124dd..0000000 --- a/.framework/python/backend/app/resources/strings.py +++ /dev/null @@ -1,25 +0,0 @@ -# API messages - -USER_DOES_NOT_EXIST_ERROR = "user does not exist" -ITEM_DOES_NOT_EXIST_ERROR = "item does not exist" -ITEM_ALREADY_EXISTS = "item already exists" -USER_IS_NOT_SELLER_OF_ITEM = "you are not an seller of this item" - -INCORRECT_LOGIN_INPUT = "incorrect email or password" -USERNAME_TAKEN = "user with this username already exists" -EMAIL_TAKEN = "user with this email already exists" - -UNABLE_TO_FOLLOW_YOURSELF = "user can not follow him self" -UNABLE_TO_UNSUBSCRIBE_FROM_YOURSELF = "user can not unsubscribe from him self" -USER_IS_NOT_FOLLOWED = "you don't follow this user" -USER_IS_ALREADY_FOLLOWED = "you follow this user already" - -WRONG_TOKEN_PREFIX = "unsupported authorization type" # noqa: S105 -MALFORMED_PAYLOAD = "could not validate credentials" - -ITEM_IS_ALREADY_FAVORITED = "you are already marked this items as favorite" -ITEM_IS_NOT_FAVORITED = "item is not favorited" - -COMMENT_DOES_NOT_EXIST = "comment does not exist" - -AUTHENTICATION_REQUIRED = "authentication required" diff --git a/.framework/python/backend/app/services/__init__.py b/.framework/python/backend/app/services/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/python/backend/app/services/authentication.py b/.framework/python/backend/app/services/authentication.py deleted file mode 100644 index 84539a6..0000000 --- a/.framework/python/backend/app/services/authentication.py +++ /dev/null @@ -1,20 +0,0 @@ -from app.db.errors import EntityDoesNotExist -from app.db.repositories.users import UsersRepository - - -async def check_username_is_taken(repo: UsersRepository, username: str) -> bool: - try: - await repo.get_user_by_username(username=username) - except EntityDoesNotExist: - return False - - return True - - -async def check_email_is_taken(repo: UsersRepository, email: str) -> bool: - try: - await repo.get_user_by_email(email=email) - except EntityDoesNotExist: - return False - - return True diff --git a/.framework/python/backend/app/services/event.py b/.framework/python/backend/app/services/event.py deleted file mode 100644 index 1de0618..0000000 --- a/.framework/python/backend/app/services/event.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -import requests -import json - -PATH_TO_WILCO_ID = '../.wilco' -BASE_URL = os.environ.get('ENGINE_BASE_URL') or 'https://engine.wilco.gg' -WILCO_ID = os.environ.get('WILCO_ID') - -if not WILCO_ID and os.path.exists(PATH_TO_WILCO_ID): - with open(PATH_TO_WILCO_ID, 'r') as f: - WILCO_ID = f.read() - -EVENTS_ENDPOINT = f'{BASE_URL}/users/{WILCO_ID}/event' - -def send_event(event, metadata): - headers = { 'Content-type': 'application/json' } - data = { 'event': event, 'metadata': metadata } - try: - res = requests.post(EVENTS_ENDPOINT, data=json.dumps(data), headers=headers) - return res - except Exception as err: - print(f"failed to send event {event} to Wilco engine") diff --git a/.framework/python/backend/app/services/items.py b/.framework/python/backend/app/services/items.py deleted file mode 100644 index b3f6f26..0000000 --- a/.framework/python/backend/app/services/items.py +++ /dev/null @@ -1,23 +0,0 @@ -from slugify import slugify - -from app.db.errors import EntityDoesNotExist -from app.db.repositories.items import ItemsRepository -from app.models.domain.items import Item -from app.models.domain.users import User - - -async def check_item_exists(items_repo: ItemsRepository, slug: str) -> bool: - try: - await items_repo.get_item_by_slug(slug=slug) - except EntityDoesNotExist: - return False - - return True - - -def get_slug_for_item(title: str) -> str: - return slugify(title) - - -def check_user_can_modify_item(item: Item, user: User) -> bool: - return item.seller.username == user.username diff --git a/.framework/python/backend/app/services/jwt.py b/.framework/python/backend/app/services/jwt.py deleted file mode 100644 index 355ecea..0000000 --- a/.framework/python/backend/app/services/jwt.py +++ /dev/null @@ -1,41 +0,0 @@ -from datetime import datetime, timedelta -from typing import Dict - -import jwt -from pydantic import ValidationError - -from app.models.domain.users import User -from app.models.schemas.jwt import JWTMeta, JWTUser - -JWT_SUBJECT = "access" -ALGORITHM = "HS256" -ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # one week - - -def create_jwt_token( - *, - jwt_content: Dict[str, str], - secret_key: str, - expires_delta: timedelta, -) -> str: - to_encode = jwt_content.copy() - expire = datetime.utcnow() + expires_delta - to_encode.update(JWTMeta(exp=expire, sub=JWT_SUBJECT).dict()) - return jwt.encode(to_encode, secret_key, algorithm=ALGORITHM) - - -def create_access_token_for_user(user: User, secret_key: str) -> str: - return create_jwt_token( - jwt_content=JWTUser(username=user.username).dict(), - secret_key=secret_key, - expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES), - ) - - -def get_username_from_token(token: str, secret_key: str) -> str: - try: - return JWTUser(**jwt.decode(token, secret_key, algorithms=[ALGORITHM])).username - except jwt.PyJWTError as decode_error: - raise ValueError("unable to decode JWT token") from decode_error - except ValidationError as validation_error: - raise ValueError("malformed payload in token") from validation_error diff --git a/.framework/python/backend/app/services/security.py b/.framework/python/backend/app/services/security.py deleted file mode 100644 index 08c523b..0000000 --- a/.framework/python/backend/app/services/security.py +++ /dev/null @@ -1,16 +0,0 @@ -import bcrypt -from passlib.context import CryptContext - -pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") - - -def generate_salt() -> str: - return bcrypt.gensalt().decode() - - -def verify_password(plain_password: str, hashed_password: str) -> bool: - return pwd_context.verify(plain_password, hashed_password) - - -def get_password_hash(password: str) -> str: - return pwd_context.hash(password) diff --git a/.framework/python/backend/poetry.lock b/.framework/python/backend/poetry.lock deleted file mode 100644 index 1bbe72f..0000000 --- a/.framework/python/backend/poetry.lock +++ /dev/null @@ -1,2136 +0,0 @@ -[[package]] -name = "aiosql" -version = "3.3.1" -description = "Simple SQL in Python" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -contextlib2 = ">=21.6.0" -typing-extensions = ">=3.7.4,<4" - -[[package]] -name = "alembic" -version = "1.7.6" -description = "A database migration tool for SQLAlchemy." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -Mako = "*" -SQLAlchemy = ">=1.3.0" - -[package.extras] -tz = ["python-dateutil"] - -[[package]] -name = "anyio" -version = "3.5.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" - -[package.extras] -doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] -test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=6.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"] -trio = ["trio (>=0.16)"] - -[[package]] -name = "asgi-lifespan" -version = "1.0.1" -description = "Programmatic startup/shutdown of ASGI apps." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -sniffio = "*" - -[[package]] -name = "asgiref" -version = "3.5.0" -description = "ASGI specs, helper code, and adapters" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] - -[[package]] -name = "astor" -version = "0.8.1" -description = "Read/rewrite/write Python ASTs" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" - -[[package]] -name = "asyncpg" -version = "0.25.0" -description = "An asyncio PostgreSQL driver" -category = "main" -optional = false -python-versions = ">=3.6.0" - -[package.extras] -dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0.15.3)"] -docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"] -test = ["pycodestyle (>=2.7.0,<2.8.0)", "flake8 (>=3.9.2,<3.10.0)", "uvloop (>=0.15.3)"] - -[[package]] -name = "atomicwrites" -version = "1.4.0" -description = "Atomic file writes." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "attrs" -version = "21.4.0" -description = "Classes Without Boilerplate" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] - -[[package]] -name = "autoflake" -version = "1.4" -description = "Removes unused imports and unused variables" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -pyflakes = ">=1.1.0" - -[[package]] -name = "bandit" -version = "1.7.2" -description = "Security oriented static analyser for python code." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -GitPython = ">=1.0.1" -PyYAML = ">=5.3.1" -stevedore = ">=1.20.0" - -[package.extras] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml"] -toml = ["toml"] -yaml = ["pyyaml"] - -[[package]] -name = "bcrypt" -version = "3.2.0" -description = "Modern password hashing for your software and your servers" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = ">=1.1" -six = ">=1.4.1" - -[package.extras] -tests = ["pytest (>=3.2.1,!=3.3.0)"] -typecheck = ["mypy"] - -[[package]] -name = "black" -version = "22.1.0" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = ">=1.1.0" -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "certifi" -version = "2021.10.8" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "cffi" -version = "1.15.0" -description = "Foreign Function Interface for Python calling C code." -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "charset-normalizer" -version = "2.0.11" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.5.0" - -[package.extras] -unicode_backport = ["unicodedata2"] - -[[package]] -name = "click" -version = "8.0.3" -description = "Composable command line interface toolkit" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.4" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "contextlib2" -version = "21.6.0" -description = "Backports and enhancements for the contextlib module" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "coverage" -version = "6.4.1" -description = "Code coverage measurement for Python" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "darglint" -version = "1.8.1" -description = "A utility for ensuring Google-style docstrings stay up to date with the source code." -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[[package]] -name = "databases" -version = "0.5.5" -description = "Async database support for Python." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -sqlalchemy = ">=1.4,<1.5" - -[package.extras] -mysql = ["aiomysql"] -mysql_asyncmy = ["asyncmy"] -postgresql = ["asyncpg"] -postgresql_aiopg = ["aiopg"] -sqlite = ["aiosqlite"] - -[[package]] -name = "dnspython" -version = "2.2.0" -description = "DNS toolkit" -category = "main" -optional = false -python-versions = ">=3.6,<4.0" - -[package.extras] -dnssec = ["cryptography (>=2.6,<37.0)"] -curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] -doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.10.0)"] -idna = ["idna (>=2.1,<4.0)"] -trio = ["trio (>=0.14,<0.20)"] -wmi = ["wmi (>=1.5.1,<2.0.0)"] - -[[package]] -name = "docutils" -version = "0.18.1" -description = "Docutils -- Python Documentation Utilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "email-validator" -version = "1.1.3" -description = "A robust email syntax and deliverability validation library for Python 2.x/3.x." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -dnspython = ">=1.15.0" -idna = ">=2.0.0" - -[[package]] -name = "eradicate" -version = "2.0.0" -description = "Removes commented-out code." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "execnet" -version = "1.9.0" -description = "execnet: rapid multi-Python deployment" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.extras] -testing = ["pre-commit"] - -[[package]] -name = "fastapi" -version = "0.73.0" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "main" -optional = false -python-versions = ">=3.6.1" - -[package.dependencies] -pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" -starlette = "0.17.1" - -[package.extras] -all = ["requests (>=2.24.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<3.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)"] -dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.16.0)"] -doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"] -test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==21.9b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==0.1.1)", "types-orjson (==3.6.0)", "types-dataclasses (==0.1.7)"] - -[[package]] -name = "flake8" -version = "3.9.2" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[package.dependencies] -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.7.0,<2.8.0" -pyflakes = ">=2.3.0,<2.4.0" - -[[package]] -name = "flake8-bandit" -version = "2.1.2" -description = "Automated security testing with bandit and flake8." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -bandit = "*" -flake8 = "*" -flake8-polyfill = "*" -pycodestyle = "*" - -[[package]] -name = "flake8-broken-line" -version = "0.3.0" -description = "Flake8 plugin to forbid backslashes for line breaks" -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -flake8 = ">=3.5,<4.0" - -[[package]] -name = "flake8-bugbear" -version = "21.11.29" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -attrs = ">=19.2.0" -flake8 = ">=3.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit"] - -[[package]] -name = "flake8-commas" -version = "2.1.0" -description = "Flake8 lint for trailing commas." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -flake8 = ">=2" - -[[package]] -name = "flake8-comprehensions" -version = "3.8.0" -description = "A flake8 plugin to help you write better list/set/dict comprehensions." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -flake8 = ">=3.0,<3.2.0 || >3.2.0" - -[[package]] -name = "flake8-debugger" -version = "4.0.0" -description = "ipdb/pdb statement checker plugin for flake8" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -flake8 = ">=3.0" -pycodestyle = "*" -six = "*" - -[[package]] -name = "flake8-docstrings" -version = "1.6.0" -description = "Extension for flake8 which uses pydocstyle to check docstrings" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -flake8 = ">=3" -pydocstyle = ">=2.1" - -[[package]] -name = "flake8-eradicate" -version = "1.2.0" -description = "Flake8 plugin to find commented out code" -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -attrs = "*" -eradicate = ">=2.0,<3.0" -flake8 = ">=3.5,<5" - -[[package]] -name = "flake8-fixme" -version = "1.1.1" -description = "Check for FIXME, TODO and other temporary developer notes. Plugin for flake8." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "flake8-isort" -version = "4.1.1" -description = "flake8 plugin that integrates isort ." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -flake8 = ">=3.2.1,<5" -isort = ">=4.3.5,<6" -testfixtures = ">=6.8.0,<7" - -[package.extras] -test = ["pytest-cov"] - -[[package]] -name = "flake8-polyfill" -version = "1.0.2" -description = "Polyfill package for Flake8 plugins" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -flake8 = "*" - -[[package]] -name = "flake8-quotes" -version = "3.3.1" -description = "Flake8 lint for quotes." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -flake8 = "*" - -[[package]] -name = "flake8-rst-docstrings" -version = "0.2.5" -description = "Python docstring reStructuredText (RST) validator" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -flake8 = ">=3.0.0" -pygments = "*" -restructuredtext-lint = "*" - -[[package]] -name = "flake8-string-format" -version = "0.3.0" -description = "string format checker, plugin for flake8" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -flake8 = "*" - -[[package]] -name = "gitdb" -version = "4.0.9" -description = "Git Object Database" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.26" -description = "GitPython is a python library used to interact with Git repositories" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[[package]] -name = "greenlet" -version = "1.1.2" -description = "Lightweight in-process concurrent programming" -category = "main" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" - -[package.extras] -docs = ["sphinx"] - -[[package]] -name = "gunicorn" -version = "20.1.0" -description = "WSGI HTTP Server for UNIX" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.extras] -eventlet = ["eventlet (>=0.24.1)"] -gevent = ["gevent (>=1.4.0)"] -setproctitle = ["setproctitle"] -tornado = ["tornado (>=0.2)"] - -[[package]] -name = "h11" -version = "0.12.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "httpcore" -version = "0.14.7" -description = "A minimal low-level HTTP client." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -anyio = ">=3.0.0,<4.0.0" -certifi = "*" -h11 = ">=0.11,<0.13" -sniffio = ">=1.0.0,<2.0.0" - -[package.extras] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] - -[[package]] -name = "httpx" -version = "0.22.0" -description = "The next generation HTTP client." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -certifi = "*" -charset-normalizer = "*" -httpcore = ">=0.14.5,<0.15.0" -rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} -sniffio = "*" - -[package.extras] -brotli = ["brotlicffi", "brotli"] -cli = ["click (>=8.0.0,<9.0.0)", "rich (>=10.0.0,<11.0.0)", "pygments (>=2.0.0,<3.0.0)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] - -[[package]] -name = "idna" -version = "3.3" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "isort" -version = "5.10.1" -description = "A Python utility / library to sort Python imports." -category = "dev" -optional = false -python-versions = ">=3.6.1,<4.0" - -[package.extras] -pipfile_deprecated_finder = ["pipreqs", "requirementslib"] -requirements_deprecated_finder = ["pipreqs", "pip-api"] -colors = ["colorama (>=0.4.3,<0.5.0)"] -plugins = ["setuptools"] - -[[package]] -name = "loguru" -version = "0.6.0" -description = "Python logging made (stupidly) simple" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} -win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} - -[package.extras] -dev = ["colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "black (>=19.10b0)", "isort (>=5.1.1)", "Sphinx (>=4.1.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)"] - -[[package]] -name = "mako" -version = "1.1.6" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -MarkupSafe = ">=0.9.2" - -[package.extras] -babel = ["babel"] -lingua = ["lingua"] - -[[package]] -name = "markupsafe" -version = "2.0.1" -description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "mypy" -version = "0.931" -description = "Optional static typing for Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -mypy-extensions = ">=0.4.3" -tomli = ">=1.1.0" -typing-extensions = ">=3.10" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -python2 = ["typed-ast (>=1.4.0,<2)"] - -[[package]] -name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "packaging" -version = "21.3" -description = "Core utilities for Python packages" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" - -[[package]] -name = "passlib" -version = "1.7.4" -description = "comprehensive password hashing framework supporting over 30 schemes" -category = "main" -optional = false -python-versions = "*" - -[package.dependencies] -bcrypt = {version = ">=3.1.0", optional = true, markers = "extra == \"bcrypt\""} - -[package.extras] -argon2 = ["argon2-cffi (>=18.2.0)"] -bcrypt = ["bcrypt (>=3.1.0)"] -build_docs = ["sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)", "cloud-sptheme (>=1.10.1)"] -totp = ["cryptography"] - -[[package]] -name = "pathspec" -version = "0.9.0" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[[package]] -name = "pbr" -version = "5.8.1" -description = "Python Build Reasonableness" -category = "dev" -optional = false -python-versions = ">=2.6" - -[[package]] -name = "pep8-naming" -version = "0.11.1" -description = "Check PEP-8 naming conventions, plugin for flake8" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -flake8-polyfill = ">=1.0.2,<2" - -[[package]] -name = "platformdirs" -version = "2.4.1" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] -test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "psycopg2-binary" -version = "2.9.3" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "pycodestyle" -version = "2.7.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pydantic" -version = "1.9.0" -description = "Data validation and settings management using python 3.6 type hinting" -category = "main" -optional = false -python-versions = ">=3.6.1" - -[package.dependencies] -email-validator = {version = ">=1.0.3", optional = true, markers = "extra == \"email\""} -python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""} -typing-extensions = ">=3.7.4.3" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pydocstyle" -version = "6.1.1" -description = "Python docstring style checker" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -snowballstemmer = "*" - -[package.extras] -toml = ["toml"] - -[[package]] -name = "pyflakes" -version = "2.3.1" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pygments" -version = "2.11.2" -description = "Pygments is a syntax highlighting package written in Python." -category = "dev" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "pyjwt" -version = "2.3.0" -description = "JSON Web Token implementation in Python" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.extras] -crypto = ["cryptography (>=3.3.1)"] -dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"] -docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] - -[[package]] -name = "pyparsing" -version = "3.0.7" -description = "Python parsing module" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pypika" -version = "0.48.8" -description = "A SQL query builder API for Python" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "pytest" -version = "7.0.0" -description = "pytest: simple powerful testing with Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -tomli = ">=1.0.0" - -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] - -[[package]] -name = "pytest-asyncio" -version = "0.18.0" -description = "Pytest support for asyncio" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -pytest = ">=6.1.0" - -[package.extras] -testing = ["coverage (==6.2)", "hypothesis (>=5.7.1)", "flaky (>=3.5.0)", "mypy (==0.931)"] - -[[package]] -name = "pytest-cov" -version = "3.0.0" -description = "Pytest plugin for measuring coverage." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] - -[[package]] -name = "pytest-env" -version = "0.6.2" -description = "py.test plugin that allows you to add environment variables." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -pytest = ">=2.6.0" - -[[package]] -name = "pytest-forked" -version = "1.4.0" -description = "run tests in isolated forked subprocesses" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -py = "*" -pytest = ">=3.10" - -[[package]] -name = "pytest-xdist" -version = "2.5.0" -description = "pytest xdist plugin for distributed testing and loop-on-failing modes" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -execnet = ">=1.1" -pytest = ">=6.2.0" -pytest-forked = "*" - -[package.extras] -psutil = ["psutil (>=3.0)"] -setproctitle = ["setproctitle"] -testing = ["filelock"] - -[[package]] -name = "python-dotenv" -version = "0.19.2" -description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "python-slugify" -version = "5.0.2" -description = "A Python Slugify application that handles Unicode" -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -text-unidecode = ">=1.3" - -[package.extras] -unidecode = ["Unidecode (>=1.1.1)"] - -[[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "requests" -version = "2.28.0" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7, <4" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2.0.0,<2.1.0" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] - -[[package]] -name = "restructuredtext-lint" -version = "1.3.2" -description = "reStructuredText linter" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -docutils = ">=0.11,<1.0" - -[[package]] -name = "rfc3986" -version = "1.5.0" -description = "Validating URI References per RFC 3986" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} - -[package.extras] -idna2008 = ["idna"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "smmap" -version = "5.0.0" -description = "A pure Python implementation of a sliding window memory map manager" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "sniffio" -version = "1.2.0" -description = "Sniff out which async library your code is running under" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "sqlalchemy" -version = "1.4.31" -description = "Database Abstraction Library" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} - -[package.extras] -aiomysql = ["greenlet (!=0.4.17)", "aiomysql"] -aiosqlite = ["typing_extensions (!=3.10.0.1)", "greenlet (!=0.4.17)", "aiosqlite"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["greenlet (!=0.4.17)", "asyncmy (>=0.2.3)"] -mariadb_connector = ["mariadb (>=1.0.1)"] -mssql = ["pyodbc"] -mssql_pymssql = ["pymssql"] -mssql_pyodbc = ["pyodbc"] -mypy = ["sqlalchemy2-stubs", "mypy (>=0.910)"] -mysql = ["mysqlclient (>=1.4.0,<2)", "mysqlclient (>=1.4.0)"] -mysql_connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"] -postgresql_pg8000 = ["pg8000 (>=1.16.6)"] -postgresql_psycopg2binary = ["psycopg2-binary"] -postgresql_psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql (<1)", "pymysql"] -sqlcipher = ["sqlcipher3-binary"] - -[[package]] -name = "starlette" -version = "0.17.1" -description = "The little ASGI library that shines." -category = "main" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -anyio = ">=3.0.0,<4" - -[package.extras] -full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] - -[[package]] -name = "stevedore" -version = "3.5.0" -description = "Manage dynamic plugins for Python applications" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pbr = ">=2.0.0,<2.1.0 || >2.1.0" - -[[package]] -name = "testfixtures" -version = "6.18.3" -description = "A collection of helpers and mock objects for unit tests and doc tests." -category = "dev" -optional = false -python-versions = "*" - -[package.extras] -build = ["setuptools-git", "wheel", "twine"] -docs = ["sphinx", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"] -test = ["pytest (>=3.6)", "pytest-cov", "pytest-django", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"] - -[[package]] -name = "text-unidecode" -version = "1.3" -description = "The most basic Text::Unidecode port" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "tomli" -version = "2.0.0" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "typing-extensions" -version = "3.10.0.2" -description = "Backported and Experimental Type Hints for Python 3.5+" -category = "main" -optional = false -python-versions = "*" - -[[package]] -name = "unidecode" -version = "1.3.2" -description = "ASCII transliterations of Unicode text" -category = "main" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "urllib3" -version = "1.26.9" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" - -[package.extras] -brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "uvicorn" -version = "0.17.4" -description = "The lightning-fast ASGI server." -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -asgiref = ">=3.4.0" -click = ">=7.0" -h11 = ">=0.8" - -[package.extras] -standard = ["websockets (>=10.0)", "httptools (>=0.2.0,<0.4.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"] - -[[package]] -name = "wemake-python-styleguide" -version = "0.16.0" -description = "The strictest and most opinionated python linter ever" -category = "dev" -optional = false -python-versions = ">=3.6,<4.0" - -[package.dependencies] -astor = ">=0.8,<0.9" -attrs = "*" -darglint = ">=1.2,<2.0" -flake8 = ">=3.7,<5" -flake8-bandit = ">=2.1,<3.0" -flake8-broken-line = ">=0.3,<0.5" -flake8-bugbear = ">=20.1,<22.0" -flake8-commas = ">=2.0,<3.0" -flake8-comprehensions = ">=3.1,<4.0" -flake8-debugger = ">=4.0,<5.0" -flake8-docstrings = ">=1.3,<2.0" -flake8-eradicate = ">=1.0,<2.0" -flake8-isort = ">=4.0,<5.0" -flake8-quotes = ">=3.0,<4.0" -flake8-rst-docstrings = ">=0.2.3,<0.3.0" -flake8-string-format = ">=0.3,<0.4" -pep8-naming = ">=0.11,<0.13" -pygments = ">=2.4,<3.0" -typing_extensions = ">=3.6,<5.0" - -[[package]] -name = "win32-setctime" -version = "1.1.0" -description = "A small Python utility to set file creation time on Windows" -category = "main" -optional = false -python-versions = ">=3.5" - -[package.extras] -dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"] - -[metadata] -lock-version = "1.1" -python-versions = "3.9.13" -content-hash = "cedb44c416663f147800d5ee734ba784fc0113120f236dc9675394eaadad0eca" - -[metadata.files] -aiosql = [ - {file = "aiosql-3.3.1-py3-none-any.whl", hash = "sha256:467067f2d237e2ccc47a3f651ae6bd128d89287a5dbf1357c065c9be37947cbc"}, - {file = "aiosql-3.3.1.tar.gz", hash = "sha256:e7144b0e96c2783b79002657497b3a16ee41068290adc1e89cf08cb3974c76fa"}, -] -alembic = [ - {file = "alembic-1.7.6-py3-none-any.whl", hash = "sha256:ad842f2c3ab5c5d4861232730779c05e33db4ba880a08b85eb505e87c01095bc"}, - {file = "alembic-1.7.6.tar.gz", hash = "sha256:6c0c05e9768a896d804387e20b299880fe01bc56484246b0dffe8075d6d3d847"}, -] -anyio = [ - {file = "anyio-3.5.0-py3-none-any.whl", hash = "sha256:b5fa16c5ff93fa1046f2eeb5bbff2dad4d3514d6cda61d02816dba34fa8c3c2e"}, - {file = "anyio-3.5.0.tar.gz", hash = "sha256:a0aeffe2fb1fdf374a8e4b471444f0f3ac4fb9f5a5b542b48824475e0042a5a6"}, -] -asgi-lifespan = [ - {file = "asgi-lifespan-1.0.1.tar.gz", hash = "sha256:9a33e7da2073c4764bc79bd6136501d6c42f60e3d2168ba71235e84122eadb7f"}, - {file = "asgi_lifespan-1.0.1-py3-none-any.whl", hash = "sha256:9ea969dc5eb5cf08e52c08dce6f61afcadd28112e72d81c972b1d8eb8691ab53"}, -] -asgiref = [ - {file = "asgiref-3.5.0-py3-none-any.whl", hash = "sha256:88d59c13d634dcffe0510be048210188edd79aeccb6a6c9028cdad6f31d730a9"}, - {file = "asgiref-3.5.0.tar.gz", hash = "sha256:2f8abc20f7248433085eda803936d98992f1343ddb022065779f37c5da0181d0"}, -] -astor = [ - {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, - {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, -] -asyncpg = [ - {file = "asyncpg-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf5e3408a14a17d480f36ebaf0401a12ff6ae5457fdf45e4e2775c51cc9517d3"}, - {file = "asyncpg-0.25.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bc197fc4aca2fd24f60241057998124012469d2e414aed3f992579db0c88e3a"}, - {file = "asyncpg-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a70783f6ffa34cc7dd2de20a873181414a34fd35a4a208a1f1a7f9f695e4ec4"}, - {file = "asyncpg-0.25.0-cp310-cp310-win32.whl", hash = "sha256:43cde84e996a3afe75f325a68300093425c2f47d340c0fc8912765cf24a1c095"}, - {file = "asyncpg-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:56d88d7ef4341412cd9c68efba323a4519c916979ba91b95d4c08799d2ff0c09"}, - {file = "asyncpg-0.25.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a84d30e6f850bac0876990bcd207362778e2208df0bee8be8da9f1558255e634"}, - {file = "asyncpg-0.25.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:beaecc52ad39614f6ca2e48c3ca15d56e24a2c15cbfdcb764a4320cc45f02fd5"}, - {file = "asyncpg-0.25.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:6f8f5fc975246eda83da8031a14004b9197f510c41511018e7b1bedde6968e92"}, - {file = "asyncpg-0.25.0-cp36-cp36m-win32.whl", hash = "sha256:ddb4c3263a8d63dcde3d2c4ac1c25206bfeb31fa83bd70fd539e10f87739dee4"}, - {file = "asyncpg-0.25.0-cp36-cp36m-win_amd64.whl", hash = "sha256:bf6dc9b55b9113f39eaa2057337ce3f9ef7de99a053b8a16360395ce588925cd"}, - {file = "asyncpg-0.25.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:acb311722352152936e58a8ee3c5b8e791b24e84cd7d777c414ff05b3530ca68"}, - {file = "asyncpg-0.25.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0a61fb196ce4dae2f2fa26eb20a778db21bbee484d2e798cb3cc988de13bdd1b"}, - {file = "asyncpg-0.25.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2633331cbc8429030b4f20f712f8d0fbba57fa8555ee9b2f45f981b81328b256"}, - {file = "asyncpg-0.25.0-cp37-cp37m-win32.whl", hash = "sha256:863d36eba4a7caa853fd7d83fad5fd5306f050cc2fe6e54fbe10cdb30420e5e9"}, - {file = "asyncpg-0.25.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fe471ccd915b739ca65e2e4dbd92a11b44a5b37f2e38f70827a1c147dafe0fa8"}, - {file = "asyncpg-0.25.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:72a1e12ea0cf7c1e02794b697e3ca967b2360eaa2ce5d4bfdd8604ec2d6b774b"}, - {file = "asyncpg-0.25.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4327f691b1bdb222df27841938b3e04c14068166b3a97491bec2cb982f49f03e"}, - {file = "asyncpg-0.25.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:739bbd7f89a2b2f6bc44cb8bf967dab12c5bc714fcbe96e68d512be45ecdf962"}, - {file = "asyncpg-0.25.0-cp38-cp38-win32.whl", hash = "sha256:18d49e2d93a7139a2fdbd113e320cc47075049997268a61bfbe0dde680c55471"}, - {file = "asyncpg-0.25.0-cp38-cp38-win_amd64.whl", hash = "sha256:191fe6341385b7fdea7dbdcf47fd6db3fd198827dcc1f2b228476d13c05a03c6"}, - {file = "asyncpg-0.25.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fab7f1b2c29e187dd8781fce896249500cf055b63471ad66332e537e9b5f7e"}, - {file = "asyncpg-0.25.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a738f1b2876f30d710d3dc1e7858160a0afe1603ba16bf5f391f5316eb0ed855"}, - {file = "asyncpg-0.25.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4105f57ad1e8fbc8b1e535d8fcefa6ce6c71081228f08680c6dea24384ff0e"}, - {file = "asyncpg-0.25.0-cp39-cp39-win32.whl", hash = "sha256:f55918ded7b85723a5eaeb34e86e7b9280d4474be67df853ab5a7fa0cc7c6bf2"}, - {file = "asyncpg-0.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:649e2966d98cc48d0646d9a4e29abecd8b59d38d55c256d5c857f6b27b7407ac"}, - {file = "asyncpg-0.25.0.tar.gz", hash = "sha256:63f8e6a69733b285497c2855464a34de657f2cccd25aeaeeb5071872e9382540"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] -attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] -autoflake = [ - {file = "autoflake-1.4.tar.gz", hash = "sha256:61a353012cff6ab94ca062823d1fb2f692c4acda51c76ff83a8d77915fba51ea"}, -] -bandit = [ - {file = "bandit-1.7.2-py3-none-any.whl", hash = "sha256:e20402cadfd126d85b68ed4c8862959663c8c372dbbb1fca8f8e2c9f55a067ec"}, - {file = "bandit-1.7.2.tar.gz", hash = "sha256:6d11adea0214a43813887bfe71a377b5a9955e4c826c8ffd341b494e3ab25260"}, -] -bcrypt = [ - {file = "bcrypt-3.2.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b589229207630484aefe5899122fb938a5b017b0f4349f769b8c13e78d99a8fd"}, - {file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"}, - {file = "bcrypt-3.2.0-cp36-abi3-manylinux1_x86_64.whl", hash = "sha256:63d4e3ff96188e5898779b6057878fecf3f11cfe6ec3b313ea09955d587ec7a7"}, - {file = "bcrypt-3.2.0-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:cd1ea2ff3038509ea95f687256c46b79f5fc382ad0aa3664d200047546d511d1"}, - {file = "bcrypt-3.2.0-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:cdcdcb3972027f83fe24a48b1e90ea4b584d35f1cc279d76de6fc4b13376239d"}, - {file = "bcrypt-3.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a0584a92329210fcd75eb8a3250c5a941633f8bfaf2a18f81009b097732839b7"}, - {file = "bcrypt-3.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:56e5da069a76470679f312a7d3d23deb3ac4519991a0361abc11da837087b61d"}, - {file = "bcrypt-3.2.0-cp36-abi3-win32.whl", hash = "sha256:a67fb841b35c28a59cebed05fbd3e80eea26e6d75851f0574a9273c80f3e9b55"}, - {file = "bcrypt-3.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:81fec756feff5b6818ea7ab031205e1d323d8943d237303baca2c5f9c7846f34"}, - {file = "bcrypt-3.2.0.tar.gz", hash = "sha256:5b93c1726e50a93a033c36e5ca7fdcd29a5c7395af50a6892f5d9e7c6cfbfb29"}, -] -black = [ - {file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"}, - {file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"}, - {file = "black-22.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71"}, - {file = "black-22.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab"}, - {file = "black-22.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5"}, - {file = "black-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a"}, - {file = "black-22.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0"}, - {file = "black-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba"}, - {file = "black-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1"}, - {file = "black-22.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8"}, - {file = "black-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28"}, - {file = "black-22.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912"}, - {file = "black-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3"}, - {file = "black-22.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3"}, - {file = "black-22.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61"}, - {file = "black-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd"}, - {file = "black-22.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f"}, - {file = "black-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0"}, - {file = "black-22.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c"}, - {file = "black-22.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2"}, - {file = "black-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321"}, - {file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"}, - {file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"}, -] -certifi = [ - {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, - {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, -] -cffi = [ - {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, - {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, - {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, - {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, - {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, - {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, - {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, - {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, - {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, - {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, - {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, - {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, - {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, - {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, - {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, - {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, - {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, - {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, - {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, - {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, - {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, - {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, - {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, - {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, - {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.0.11.tar.gz", hash = "sha256:98398a9d69ee80548c762ba991a4728bfc3836768ed226b3945908d1a688371c"}, - {file = "charset_normalizer-2.0.11-py3-none-any.whl", hash = "sha256:2842d8f5e82a1f6aa437380934d5e1cd4fcf2003b06fed6940769c164a480a45"}, -] -click = [ - {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, - {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, -] -contextlib2 = [ - {file = "contextlib2-21.6.0-py2.py3-none-any.whl", hash = "sha256:3fbdb64466afd23abaf6c977627b75b6139a5a3e8ce38405c5b413aed7a0471f"}, - {file = "contextlib2-21.6.0.tar.gz", hash = "sha256:ab1e2bfe1d01d968e1b7e8d9023bc51ef3509bba217bb730cee3827e1ee82869"}, -] -coverage = [ - {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"}, - {file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"}, - {file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"}, - {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"}, - {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"}, - {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"}, - {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"}, - {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"}, - {file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"}, - {file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"}, - {file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"}, - {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"}, - {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"}, - {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"}, - {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"}, - {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"}, - {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"}, - {file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"}, - {file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"}, - {file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"}, - {file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"}, - {file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"}, - {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"}, - {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"}, - {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"}, - {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"}, - {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"}, - {file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"}, - {file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"}, - {file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"}, - {file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"}, - {file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"}, - {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"}, - {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"}, - {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"}, - {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"}, - {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"}, - {file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"}, - {file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"}, - {file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"}, - {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"}, -] -darglint = [ - {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, - {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, -] -databases = [ - {file = "databases-0.5.5-py3-none-any.whl", hash = "sha256:97d9b9647216d1ab53ca61c059412b5c7b6e1f0bf8ce985477982ebcc7f278f3"}, - {file = "databases-0.5.5.tar.gz", hash = "sha256:02c6b016c1c951c21cca281dc8e2e002c60dc44026c0084aabbd8c37514aeb37"}, -] -dnspython = [ - {file = "dnspython-2.2.0-py3-none-any.whl", hash = "sha256:081649da27ced5e75709a1ee542136eaba9842a0fe4c03da4fb0a3d3ed1f3c44"}, - {file = "dnspython-2.2.0.tar.gz", hash = "sha256:e79351e032d0b606b98d38a4b0e6e2275b31a5b85c873e587cc11b73aca026d6"}, -] -docutils = [ - {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, - {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, -] -email-validator = [ - {file = "email_validator-1.1.3-py2.py3-none-any.whl", hash = "sha256:5675c8ceb7106a37e40e2698a57c056756bf3f272cfa8682a4f87ebd95d8440b"}, - {file = "email_validator-1.1.3.tar.gz", hash = "sha256:aa237a65f6f4da067119b7df3f13e89c25c051327b2b5b66dc075f33d62480d7"}, -] -eradicate = [ - {file = "eradicate-2.0.0.tar.gz", hash = "sha256:27434596f2c5314cc9b31410c93d8f7e8885747399773cd088d3adea647a60c8"}, -] -execnet = [ - {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, - {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, -] -fastapi = [ - {file = "fastapi-0.73.0-py3-none-any.whl", hash = "sha256:f0a618aff5f6942862f2d3f20f39b1c037e33314d1b8207fd1c3a2cca76dfd8c"}, - {file = "fastapi-0.73.0.tar.gz", hash = "sha256:dcfee92a7f9a72b5d4b7ca364bd2b009f8fc10d95ed5769be20e94f39f7e5a15"}, -] -flake8 = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, -] -flake8-bandit = [ - {file = "flake8_bandit-2.1.2.tar.gz", hash = "sha256:687fc8da2e4a239b206af2e54a90093572a60d0954f3054e23690739b0b0de3b"}, -] -flake8-broken-line = [ - {file = "flake8-broken-line-0.3.0.tar.gz", hash = "sha256:f74e052833324a9e5f0055032f7ccc54b23faabafe5a26241c2f977e70b10b50"}, - {file = "flake8_broken_line-0.3.0-py3-none-any.whl", hash = "sha256:611f79c7f27118e7e5d3dc098ef7681c40aeadf23783700c5dbee840d2baf3af"}, -] -flake8-bugbear = [ - {file = "flake8-bugbear-21.11.29.tar.gz", hash = "sha256:8b04cb2fafc6a78e1a9d873bd3988e4282f7959bb6b0d7c1ae648ec09b937a7b"}, - {file = "flake8_bugbear-21.11.29-py36.py37.py38-none-any.whl", hash = "sha256:179e41ddae5de5e3c20d1f61736feeb234e70958fbb56ab3c28a67739c8e9a82"}, -] -flake8-commas = [ - {file = "flake8-commas-2.1.0.tar.gz", hash = "sha256:940441ab8ee544df564ae3b3f49f20462d75d5c7cac2463e0b27436e2050f263"}, - {file = "flake8_commas-2.1.0-py2.py3-none-any.whl", hash = "sha256:ebb96c31e01d0ef1d0685a21f3f0e2f8153a0381430e748bf0bbbb5d5b453d54"}, -] -flake8-comprehensions = [ - {file = "flake8-comprehensions-3.8.0.tar.gz", hash = "sha256:8e108707637b1d13734f38e03435984f6b7854fa6b5a4e34f93e69534be8e521"}, - {file = "flake8_comprehensions-3.8.0-py3-none-any.whl", hash = "sha256:9406314803abe1193c064544ab14fdc43c58424c0882f6ff8a581eb73fc9bb58"}, -] -flake8-debugger = [ - {file = "flake8-debugger-4.0.0.tar.gz", hash = "sha256:e43dc777f7db1481db473210101ec2df2bd39a45b149d7218a618e954177eda6"}, - {file = "flake8_debugger-4.0.0-py3-none-any.whl", hash = "sha256:82e64faa72e18d1bdd0000407502ebb8ecffa7bc027c62b9d4110ce27c091032"}, -] -flake8-docstrings = [ - {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"}, - {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"}, -] -flake8-eradicate = [ - {file = "flake8-eradicate-1.2.0.tar.gz", hash = "sha256:acaa1b6839ff00d284b805c432fdfa6047262bd15a5504ec945797e87b4de1fa"}, - {file = "flake8_eradicate-1.2.0-py3-none-any.whl", hash = "sha256:51dc660d0c1c1ed93af0f813540bbbf72ab2d3466c14e3f3bac371c618b6042f"}, -] -flake8-fixme = [ - {file = "flake8-fixme-1.1.1.tar.gz", hash = "sha256:50cade07d27a4c30d4f12351478df87339e67640c83041b664724bda6d16f33a"}, - {file = "flake8_fixme-1.1.1-py2.py3-none-any.whl", hash = "sha256:226a6f2ef916730899f29ac140bed5d4a17e5aba79f00a0e3ae1eff1997cb1ac"}, -] -flake8-isort = [ - {file = "flake8-isort-4.1.1.tar.gz", hash = "sha256:d814304ab70e6e58859bc5c3e221e2e6e71c958e7005239202fee19c24f82717"}, - {file = "flake8_isort-4.1.1-py3-none-any.whl", hash = "sha256:c4e8b6dcb7be9b71a02e6e5d4196cefcef0f3447be51e82730fb336fff164949"}, -] -flake8-polyfill = [ - {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, - {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, -] -flake8-quotes = [ - {file = "flake8-quotes-3.3.1.tar.gz", hash = "sha256:633adca6fb8a08131536af0d750b44d6985b9aba46f498871e21588c3e6f525a"}, -] -flake8-rst-docstrings = [ - {file = "flake8-rst-docstrings-0.2.5.tar.gz", hash = "sha256:4fe93f997dea45d9d3c8bd220f12f0b6c359948fb943b5b48021a3f927edd816"}, - {file = "flake8_rst_docstrings-0.2.5-py3-none-any.whl", hash = "sha256:b99d9041b769b857efe45a448dc8c71b1bb311f9cacbdac5de82f96498105082"}, -] -flake8-string-format = [ - {file = "flake8-string-format-0.3.0.tar.gz", hash = "sha256:65f3da786a1461ef77fca3780b314edb2853c377f2e35069723348c8917deaa2"}, - {file = "flake8_string_format-0.3.0-py2.py3-none-any.whl", hash = "sha256:812ff431f10576a74c89be4e85b8e075a705be39bc40c4b4278b5b13e2afa9af"}, -] -gitdb = [ - {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, - {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, -] -gitpython = [ - {file = "GitPython-3.1.26-py3-none-any.whl", hash = "sha256:26ac35c212d1f7b16036361ca5cff3ec66e11753a0d677fb6c48fa4e1a9dd8d6"}, - {file = "GitPython-3.1.26.tar.gz", hash = "sha256:fc8868f63a2e6d268fb25f481995ba185a85a66fcad126f039323ff6635669ee"}, -] -greenlet = [ - {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"}, - {file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"}, - {file = "greenlet-1.1.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d"}, - {file = "greenlet-1.1.2-cp27-cp27m-win32.whl", hash = "sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713"}, - {file = "greenlet-1.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40"}, - {file = "greenlet-1.1.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d"}, - {file = "greenlet-1.1.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8"}, - {file = "greenlet-1.1.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"}, - {file = "greenlet-1.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965"}, - {file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"}, - {file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"}, - {file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"}, - {file = "greenlet-1.1.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c"}, - {file = "greenlet-1.1.2-cp35-cp35m-win32.whl", hash = "sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963"}, - {file = "greenlet-1.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e"}, - {file = "greenlet-1.1.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"}, - {file = "greenlet-1.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f"}, - {file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"}, - {file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"}, - {file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"}, - {file = "greenlet-1.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe"}, - {file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"}, - {file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"}, - {file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"}, - {file = "greenlet-1.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2"}, - {file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"}, - {file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"}, - {file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"}, - {file = "greenlet-1.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3"}, - {file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"}, - {file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"}, - {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, -] -gunicorn = [ - {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, - {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, -] -h11 = [ - {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, - {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, -] -httpcore = [ - {file = "httpcore-0.14.7-py3-none-any.whl", hash = "sha256:47d772f754359e56dd9d892d9593b6f9870a37aeb8ba51e9a88b09b3d68cfade"}, - {file = "httpcore-0.14.7.tar.gz", hash = "sha256:7503ec1c0f559066e7e39bc4003fd2ce023d01cf51793e3c173b864eb456ead1"}, -] -httpx = [ - {file = "httpx-0.22.0-py3-none-any.whl", hash = "sha256:e35e83d1d2b9b2a609ef367cc4c1e66fd80b750348b20cc9e19d1952fc2ca3f6"}, - {file = "httpx-0.22.0.tar.gz", hash = "sha256:d8e778f76d9bbd46af49e7f062467e3157a5a3d2ae4876a4bbfd8a51ed9c9cb4"}, -] -idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -isort = [ - {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, - {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, -] -loguru = [ - {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"}, - {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"}, -] -mako = [ - {file = "Mako-1.1.6-py2.py3-none-any.whl", hash = "sha256:afaf8e515d075b22fad7d7b8b30e4a1c90624ff2f3733a06ec125f5a5f043a57"}, - {file = "Mako-1.1.6.tar.gz", hash = "sha256:4e9e345a41924a954251b95b4b28e14a301145b544901332e658907a7464b6b2"}, -] -markupsafe = [ - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, - {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, - {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -mypy = [ - {file = "mypy-0.931-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a"}, - {file = "mypy-0.931-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00"}, - {file = "mypy-0.931-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:300717a07ad09525401a508ef5d105e6b56646f7942eb92715a1c8d610149714"}, - {file = "mypy-0.931-cp310-cp310-win_amd64.whl", hash = "sha256:7b3f6f557ba4afc7f2ce6d3215d5db279bcf120b3cfd0add20a5d4f4abdae5bc"}, - {file = "mypy-0.931-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1bf752559797c897cdd2c65f7b60c2b6969ffe458417b8d947b8340cc9cec08d"}, - {file = "mypy-0.931-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4365c60266b95a3f216a3047f1d8e3f895da6c7402e9e1ddfab96393122cc58d"}, - {file = "mypy-0.931-cp36-cp36m-win_amd64.whl", hash = "sha256:1b65714dc296a7991000b6ee59a35b3f550e0073411ac9d3202f6516621ba66c"}, - {file = "mypy-0.931-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e839191b8da5b4e5d805f940537efcaa13ea5dd98418f06dc585d2891d228cf0"}, - {file = "mypy-0.931-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:50c7346a46dc76a4ed88f3277d4959de8a2bd0a0fa47fa87a4cde36fe247ac05"}, - {file = "mypy-0.931-cp37-cp37m-win_amd64.whl", hash = "sha256:d8f1ff62f7a879c9fe5917b3f9eb93a79b78aad47b533911b853a757223f72e7"}, - {file = "mypy-0.931-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9fe20d0872b26c4bba1c1be02c5340de1019530302cf2dcc85c7f9fc3252ae0"}, - {file = "mypy-0.931-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1b06268df7eb53a8feea99cbfff77a6e2b205e70bf31743e786678ef87ee8069"}, - {file = "mypy-0.931-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8c11003aaeaf7cc2d0f1bc101c1cc9454ec4cc9cb825aef3cafff8a5fdf4c799"}, - {file = "mypy-0.931-cp38-cp38-win_amd64.whl", hash = "sha256:d9d2b84b2007cea426e327d2483238f040c49405a6bf4074f605f0156c91a47a"}, - {file = "mypy-0.931-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ff3bf387c14c805ab1388185dd22d6b210824e164d4bb324b195ff34e322d166"}, - {file = "mypy-0.931-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b56154f8c09427bae082b32275a21f500b24d93c88d69a5e82f3978018a0266"}, - {file = "mypy-0.931-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ca7f8c4b1584d63c9a0f827c37ba7a47226c19a23a753d52e5b5eddb201afcd"}, - {file = "mypy-0.931-cp39-cp39-win_amd64.whl", hash = "sha256:74f7eccbfd436abe9c352ad9fb65872cc0f1f0a868e9d9c44db0893440f0c697"}, - {file = "mypy-0.931-py3-none-any.whl", hash = "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d"}, - {file = "mypy-0.931.tar.gz", hash = "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -passlib = [ - {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"}, - {file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"}, -] -pathspec = [ - {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, - {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, -] -pbr = [ - {file = "pbr-5.8.1-py2.py3-none-any.whl", hash = "sha256:27108648368782d07bbf1cb468ad2e2eeef29086affd14087a6d04b7de8af4ec"}, - {file = "pbr-5.8.1.tar.gz", hash = "sha256:66bc5a34912f408bb3925bf21231cb6f59206267b7f63f3503ef865c1a292e25"}, -] -pep8-naming = [ - {file = "pep8-naming-0.11.1.tar.gz", hash = "sha256:a1dd47dd243adfe8a83616e27cf03164960b507530f155db94e10b36a6cd6724"}, - {file = "pep8_naming-0.11.1-py2.py3-none-any.whl", hash = "sha256:f43bfe3eea7e0d73e8b5d07d6407ab47f2476ccaeff6937c84275cd30b016738"}, -] -platformdirs = [ - {file = "platformdirs-2.4.1-py3-none-any.whl", hash = "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca"}, - {file = "platformdirs-2.4.1.tar.gz", hash = "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -psycopg2-binary = [ - {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-win32.whl", hash = "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pycodestyle = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, -] -pycparser = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] -pydantic = [ - {file = "pydantic-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5"}, - {file = "pydantic-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4"}, - {file = "pydantic-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab624700dc145aa809e6f3ec93fb8e7d0f99d9023b713f6a953637429b437d37"}, - {file = "pydantic-1.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c8d7da6f1c1049eefb718d43d99ad73100c958a5367d30b9321b092771e96c25"}, - {file = "pydantic-1.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3c3b035103bd4e2e4a28da9da7ef2fa47b00ee4a9cf4f1a735214c1bcd05e0f6"}, - {file = "pydantic-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3011b975c973819883842c5ab925a4e4298dffccf7782c55ec3580ed17dc464c"}, - {file = "pydantic-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:086254884d10d3ba16da0588604ffdc5aab3f7f09557b998373e885c690dd398"}, - {file = "pydantic-1.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0fe476769acaa7fcddd17cadd172b156b53546ec3614a4d880e5d29ea5fbce65"}, - {file = "pydantic-1.9.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8e9dcf1ac499679aceedac7e7ca6d8641f0193c591a2d090282aaf8e9445a46"}, - {file = "pydantic-1.9.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1e4c28f30e767fd07f2ddc6f74f41f034d1dd6bc526cd59e63a82fe8bb9ef4c"}, - {file = "pydantic-1.9.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c86229333cabaaa8c51cf971496f10318c4734cf7b641f08af0a6fbf17ca3054"}, - {file = "pydantic-1.9.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:c0727bda6e38144d464daec31dff936a82917f431d9c39c39c60a26567eae3ed"}, - {file = "pydantic-1.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:dee5ef83a76ac31ab0c78c10bd7d5437bfdb6358c95b91f1ba7ff7b76f9996a1"}, - {file = "pydantic-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9c9bdb3af48e242838f9f6e6127de9be7063aad17b32215ccc36a09c5cf1070"}, - {file = "pydantic-1.9.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ee7e3209db1e468341ef41fe263eb655f67f5c5a76c924044314e139a1103a2"}, - {file = "pydantic-1.9.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b6037175234850ffd094ca77bf60fb54b08b5b22bc85865331dd3bda7a02fa1"}, - {file = "pydantic-1.9.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b2571db88c636d862b35090ccf92bf24004393f85c8870a37f42d9f23d13e032"}, - {file = "pydantic-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8b5ac0f1c83d31b324e57a273da59197c83d1bb18171e512908fe5dc7278a1d6"}, - {file = "pydantic-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bbbc94d0c94dd80b3340fc4f04fd4d701f4b038ebad72c39693c794fd3bc2d9d"}, - {file = "pydantic-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e0896200b6a40197405af18828da49f067c2fa1f821491bc8f5bde241ef3f7d7"}, - {file = "pydantic-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bdfdadb5994b44bd5579cfa7c9b0e1b0e540c952d56f627eb227851cda9db77"}, - {file = "pydantic-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:574936363cd4b9eed8acdd6b80d0143162f2eb654d96cb3a8ee91d3e64bf4cf9"}, - {file = "pydantic-1.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c556695b699f648c58373b542534308922c46a1cda06ea47bc9ca45ef5b39ae6"}, - {file = "pydantic-1.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f947352c3434e8b937e3aa8f96f47bdfe6d92779e44bb3f41e4c213ba6a32145"}, - {file = "pydantic-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5e48ef4a8b8c066c4a31409d91d7ca372a774d0212da2787c0d32f8045b1e034"}, - {file = "pydantic-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:96f240bce182ca7fe045c76bcebfa0b0534a1bf402ed05914a6f1dadff91877f"}, - {file = "pydantic-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:815ddebb2792efd4bba5488bc8fde09c29e8ca3227d27cf1c6990fc830fd292b"}, - {file = "pydantic-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c5b77947b9e85a54848343928b597b4f74fc364b70926b3c4441ff52620640c"}, - {file = "pydantic-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c68c3bc88dbda2a6805e9a142ce84782d3930f8fdd9655430d8576315ad97ce"}, - {file = "pydantic-1.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a79330f8571faf71bf93667d3ee054609816f10a259a109a0738dac983b23c3"}, - {file = "pydantic-1.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f5a64b64ddf4c99fe201ac2724daada8595ada0d102ab96d019c1555c2d6441d"}, - {file = "pydantic-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a733965f1a2b4090a5238d40d983dcd78f3ecea221c7af1497b845a9709c1721"}, - {file = "pydantic-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cc6a4cb8a118ffec2ca5fcb47afbacb4f16d0ab8b7350ddea5e8ef7bcc53a16"}, - {file = "pydantic-1.9.0-py3-none-any.whl", hash = "sha256:085ca1de245782e9b46cefcf99deecc67d418737a1fd3f6a4f511344b613a5b3"}, - {file = "pydantic-1.9.0.tar.gz", hash = "sha256:742645059757a56ecd886faf4ed2441b9c0cd406079c2b4bee51bcc3fbcd510a"}, -] -pydocstyle = [ - {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, - {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, -] -pyflakes = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, -] -pygments = [ - {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, - {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, -] -pyjwt = [ - {file = "PyJWT-2.3.0-py3-none-any.whl", hash = "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f"}, - {file = "PyJWT-2.3.0.tar.gz", hash = "sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41"}, -] -pyparsing = [ - {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, - {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, -] -pypika = [ - {file = "pypika-0.48.8.tar.gz", hash = "sha256:45af481d8523d60f87e308dee6ff5c454f331c8ce3a675e5398fbea6c20fe1b1"}, -] -pytest = [ - {file = "pytest-7.0.0-py3-none-any.whl", hash = "sha256:42901e6bd4bd4a0e533358a86e848427a49005a3256f657c5c8f8dd35ef137a9"}, - {file = "pytest-7.0.0.tar.gz", hash = "sha256:dad48ffda394e5ad9aa3b7d7ddf339ed502e5e365b1350e0af65f4a602344b11"}, -] -pytest-asyncio = [ - {file = "pytest-asyncio-0.18.0.tar.gz", hash = "sha256:5c510e5d3ad0f97bab0ae0223363d2aa6329bbbafb0981d96dbed6a804a99349"}, - {file = "pytest_asyncio-0.18.0-py3-none-any.whl", hash = "sha256:5e33f5010402309ff4e8cdec04e76b057ae73e0c132f12c6aa2fa6ec8cabfbf1"}, -] -pytest-cov = [ - {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, - {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, -] -pytest-env = [ - {file = "pytest-env-0.6.2.tar.gz", hash = "sha256:7e94956aef7f2764f3c147d216ce066bf6c42948bb9e293169b1b1c880a580c2"}, -] -pytest-forked = [ - {file = "pytest-forked-1.4.0.tar.gz", hash = "sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e"}, - {file = "pytest_forked-1.4.0-py3-none-any.whl", hash = "sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"}, -] -pytest-xdist = [ - {file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"}, - {file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"}, -] -python-dotenv = [ - {file = "python-dotenv-0.19.2.tar.gz", hash = "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f"}, - {file = "python_dotenv-0.19.2-py2.py3-none-any.whl", hash = "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3"}, -] -python-slugify = [ - {file = "python-slugify-5.0.2.tar.gz", hash = "sha256:f13383a0b9fcbe649a1892b9c8eb4f8eab1d6d84b84bb7a624317afa98159cab"}, - {file = "python_slugify-5.0.2-py2.py3-none-any.whl", hash = "sha256:6d8c5df75cd4a7c3a2d21e257633de53f52ab0265cd2d1dc62a730e8194a7380"}, -] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -requests = [ - {file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"}, - {file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"}, -] -restructuredtext-lint = [ - {file = "restructuredtext_lint-1.3.2.tar.gz", hash = "sha256:d3b10a1fe2ecac537e51ae6d151b223b78de9fafdd50e5eb6b08c243df173c80"}, -] -rfc3986 = [ - {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, - {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -smmap = [ - {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, - {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, -] -sniffio = [ - {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, - {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, -] -snowballstemmer = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] -sqlalchemy = [ - {file = "SQLAlchemy-1.4.31-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c3abc34fed19fdeaead0ced8cf56dd121f08198008c033596aa6aae7cc58f59f"}, - {file = "SQLAlchemy-1.4.31-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8d0949b11681380b4a50ac3cd075e4816afe9fa4a8c8ae006c1ca26f0fa40ad8"}, - {file = "SQLAlchemy-1.4.31-cp27-cp27m-win32.whl", hash = "sha256:f3b7ec97e68b68cb1f9ddb82eda17b418f19a034fa8380a0ac04e8fe01532875"}, - {file = "SQLAlchemy-1.4.31-cp27-cp27m-win_amd64.whl", hash = "sha256:81f2dd355b57770fdf292b54f3e0a9823ec27a543f947fa2eb4ec0df44f35f0d"}, - {file = "SQLAlchemy-1.4.31-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4ad31cec8b49fd718470328ad9711f4dc703507d434fd45461096da0a7135ee0"}, - {file = "SQLAlchemy-1.4.31-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:05fa14f279d43df68964ad066f653193187909950aa0163320b728edfc400167"}, - {file = "SQLAlchemy-1.4.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dccff41478050e823271642837b904d5f9bda3f5cf7d371ce163f00a694118d6"}, - {file = "SQLAlchemy-1.4.31-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57205844f246bab9b666a32f59b046add8995c665d9ecb2b7b837b087df90639"}, - {file = "SQLAlchemy-1.4.31-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea8210090a816d48a4291a47462bac750e3bc5c2442e6d64f7b8137a7c3f9ac5"}, - {file = "SQLAlchemy-1.4.31-cp310-cp310-win32.whl", hash = "sha256:2e216c13ecc7fcdcbb86bb3225425b3ed338e43a8810c7089ddb472676124b9b"}, - {file = "SQLAlchemy-1.4.31-cp310-cp310-win_amd64.whl", hash = "sha256:e3a86b59b6227ef72ffc10d4b23f0fe994bef64d4667eab4fb8cd43de4223bec"}, - {file = "SQLAlchemy-1.4.31-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:2fd4d3ca64c41dae31228b80556ab55b6489275fb204827f6560b65f95692cf3"}, - {file = "SQLAlchemy-1.4.31-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f22c040d196f841168b1456e77c30a18a3dc16b336ddbc5a24ce01ab4e95ae0"}, - {file = "SQLAlchemy-1.4.31-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c0c7171aa5a57e522a04a31b84798b6c926234cb559c0939840c3235cf068813"}, - {file = "SQLAlchemy-1.4.31-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d046a9aeba9bc53e88a41e58beb72b6205abb9a20f6c136161adf9128e589db5"}, - {file = "SQLAlchemy-1.4.31-cp36-cp36m-win32.whl", hash = "sha256:d86132922531f0dc5a4f424c7580a472a924dd737602638e704841c9cb24aea2"}, - {file = "SQLAlchemy-1.4.31-cp36-cp36m-win_amd64.whl", hash = "sha256:ca68c52e3cae491ace2bf39b35fef4ce26c192fd70b4cd90f040d419f70893b5"}, - {file = "SQLAlchemy-1.4.31-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:cf2cd387409b12d0a8b801610d6336ee7d24043b6dd965950eaec09b73e7262f"}, - {file = "SQLAlchemy-1.4.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb4b15fb1f0aafa65cbdc62d3c2078bea1ceecbfccc9a1f23a2113c9ac1191fa"}, - {file = "SQLAlchemy-1.4.31-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c317ddd7c586af350a6aef22b891e84b16bff1a27886ed5b30f15c1ed59caeaa"}, - {file = "SQLAlchemy-1.4.31-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c7ed6c69debaf6198fadb1c16ae1253a29a7670bbf0646f92582eb465a0b999"}, - {file = "SQLAlchemy-1.4.31-cp37-cp37m-win32.whl", hash = "sha256:6a01ec49ca54ce03bc14e10de55dfc64187a2194b3b0e5ac0fdbe9b24767e79e"}, - {file = "SQLAlchemy-1.4.31-cp37-cp37m-win_amd64.whl", hash = "sha256:330eb45395874cc7787214fdd4489e2afb931bc49e0a7a8f9cd56d6e9c5b1639"}, - {file = "SQLAlchemy-1.4.31-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5e9c7b3567edbc2183607f7d9f3e7e89355b8f8984eec4d2cd1e1513c8f7b43f"}, - {file = "SQLAlchemy-1.4.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de85c26a5a1c72e695ab0454e92f60213b4459b8d7c502e0be7a6369690eeb1a"}, - {file = "SQLAlchemy-1.4.31-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:975f5c0793892c634c4920057da0de3a48bbbbd0a5c86f5fcf2f2fedf41b76da"}, - {file = "SQLAlchemy-1.4.31-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5c20c8415173b119762b6110af64448adccd4d11f273fb9f718a9865b88a99c"}, - {file = "SQLAlchemy-1.4.31-cp38-cp38-win32.whl", hash = "sha256:b35dca159c1c9fa8a5f9005e42133eed82705bf8e243da371a5e5826440e65ca"}, - {file = "SQLAlchemy-1.4.31-cp38-cp38-win_amd64.whl", hash = "sha256:b7b20c88873675903d6438d8b33fba027997193e274b9367421e610d9da76c08"}, - {file = "SQLAlchemy-1.4.31-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:85e4c244e1de056d48dae466e9baf9437980c19fcde493e0db1a0a986e6d75b4"}, - {file = "SQLAlchemy-1.4.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79e73d5ee24196d3057340e356e6254af4d10e1fc22d3207ea8342fc5ffb977"}, - {file = "SQLAlchemy-1.4.31-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:15a03261aa1e68f208e71ae3cd845b00063d242cbf8c87348a0c2c0fc6e1f2ac"}, - {file = "SQLAlchemy-1.4.31-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddc5e5ccc0160e7ad190e5c61eb57560f38559e22586955f205e537cda26034"}, - {file = "SQLAlchemy-1.4.31-cp39-cp39-win32.whl", hash = "sha256:289465162b1fa1e7a982f8abe59d26a8331211cad4942e8031d2b7db1f75e649"}, - {file = "SQLAlchemy-1.4.31-cp39-cp39-win_amd64.whl", hash = "sha256:9e4fb2895b83993831ba2401b6404de953fdbfa9d7d4fa6a4756294a83bbc94f"}, - {file = "SQLAlchemy-1.4.31.tar.gz", hash = "sha256:582b59d1e5780a447aada22b461e50b404a9dc05768da1d87368ad8190468418"}, -] -starlette = [ - {file = "starlette-0.17.1-py3-none-any.whl", hash = "sha256:26a18cbda5e6b651c964c12c88b36d9898481cd428ed6e063f5f29c418f73050"}, - {file = "starlette-0.17.1.tar.gz", hash = "sha256:57eab3cc975a28af62f6faec94d355a410634940f10b30d68d31cb5ec1b44ae8"}, -] -stevedore = [ - {file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"}, - {file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"}, -] -testfixtures = [ - {file = "testfixtures-6.18.3-py2.py3-none-any.whl", hash = "sha256:6ddb7f56a123e1a9339f130a200359092bd0a6455e31838d6c477e8729bb7763"}, - {file = "testfixtures-6.18.3.tar.gz", hash = "sha256:2600100ae96ffd082334b378e355550fef8b4a529a6fa4c34f47130905c7426d"}, -] -text-unidecode = [ - {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, - {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, -] -tomli = [ - {file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"}, - {file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"}, -] -typing-extensions = [ - {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, - {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, - {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, -] -unidecode = [ - {file = "Unidecode-1.3.2-py3-none-any.whl", hash = "sha256:215fe33c9d1c889fa823ccb66df91b02524eb8cc8c9c80f9c5b8129754d27829"}, - {file = "Unidecode-1.3.2.tar.gz", hash = "sha256:669898c1528912bcf07f9819dc60df18d057f7528271e31f8ec28cc88ef27504"}, -] -urllib3 = [ - {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, - {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, -] -uvicorn = [ - {file = "uvicorn-0.17.4-py3-none-any.whl", hash = "sha256:e85872d84fb651cccc4c5d2a71cf7ead055b8fb4d8f1e78e36092282c0cf2aec"}, - {file = "uvicorn-0.17.4.tar.gz", hash = "sha256:25850bbc86195a71a6477b3e4b3b7b4c861fb687fb96912972ce5324472b1011"}, -] -wemake-python-styleguide = [ - {file = "wemake-python-styleguide-0.16.0.tar.gz", hash = "sha256:3bf0a4962404e6fd6fa479e72e2ba3fb75d5920ea6c44b72b45240c9e519543c"}, - {file = "wemake_python_styleguide-0.16.0-py3-none-any.whl", hash = "sha256:8caa92b4aa77b08a505d718553238812d1b612b1036bc171ca3aa18345efe0b4"}, -] -win32-setctime = [ - {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, - {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, -] diff --git a/.framework/python/backend/pyproject.toml b/.framework/python/backend/pyproject.toml deleted file mode 100644 index 926fa1d..0000000 --- a/.framework/python/backend/pyproject.toml +++ /dev/null @@ -1,72 +0,0 @@ -[tool.poetry] -name = "Anythink Market Backend" -version = "0.0.0" -description = "Backend logic implementation for Anythink Market" -authors=["Anythink"] -license = "MIT" - -[tool.poetry.dependencies] -python = "3.9.13" -uvicorn = "^0.17.4" -fastapi = "^0.73.0" -pydantic = { version = "^1.8", extras = ["email", "dotenv"] } -passlib = { version = "^1.7", extras = ["bcrypt"] } -pyjwt = "^2.3" -databases = "^0.5.5" -asyncpg = "^0.25.0" -psycopg2-binary = "^2.9.3" -aiosql = "^3.3.1" -pypika = "^0.48.8" -alembic = "^1.7" -python-slugify = "^5.0" -Unidecode = "^1.3" -loguru = "^0.6.0" -requests = "^2.28.0" -gunicorn = "^20.1.0" - -[tool.poetry.dev-dependencies] -black = "^22.1.0" -isort = "^5.10" -autoflake = "^1.4" -wemake-python-styleguide = "^0.16.0" -mypy = "^0.931" -flake8-fixme = "^1.1" -pytest = "^7.0" -pytest-cov = "^3.0" -pytest-asyncio = "^0.18.0" -pytest-env = "^0.6.2" -pytest-xdist = "^2.4.0" -httpx = "^0.22.0" -asgi-lifespan = "^1.0.1" - -[tool.isort] -profile = "black" -src_paths = ["app", "tests"] -combine_as_imports = true - -[tool.pytest.ini_options] -testpaths = "tests" -filterwarnings = "error" -addopts = ''' - --strict-markers - --tb=short - --cov=app - --cov=tests - --cov-branch - --cov-report=term-missing - --cov-report=html - --cov-report=xml - --no-cov-on-fail - --cov-fail-under=100 - --numprocesses=auto - --asyncio-mode=auto -''' -env = [ - "SECRET_KEY=e6F9KvSDf4dyXj", - "MAX_CONNECTIONS_COUNT=1", - "MIN_CONNECTIONS_COUNT=1" -] - -[build-system] -requires = ["poetry>=1.0"] -build-backend = "poetry.masonry.api" diff --git a/.framework/python/backend/requirements.txt b/.framework/python/backend/requirements.txt deleted file mode 100644 index 4731c9d..0000000 --- a/.framework/python/backend/requirements.txt +++ /dev/null @@ -1,43 +0,0 @@ -aiosql==3.3.1 -alembic==1.7.6 -anyio==3.5.0 -asgiref==3.5.0 -asyncpg==0.25.0 -bcrypt==3.2.0 -certifi==2021.10.8 -cffi==1.15.0 -charset-normalizer==2.0.11 -click==8.0.3 -colorama==0.4.4; platform_system == "Windows" -colorama==0.4.4; sys_platform == "win32" -contextlib2==21.6.0 -databases==0.5.5 -dnspython==2.2.0 -email-validator==1.1.3 -fastapi==0.73.0 -greenlet==1.1.2; python_version >= "3" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") -gunicorn==20.1.0 -h11==0.12.0 -idna==3.3 -loguru==0.6.0 -mako==1.1.6 -markupsafe==2.0.1 -passlib==1.7.4 -psycopg2-binary==2.9.3 -pycparser==2.21 -pydantic==1.9.0 -pyjwt==2.3.0 -pypika==0.48.8 -python-dotenv==0.19.2 -python-slugify==5.0.2 -requests==2.28.0 -six==1.16.0 -sniffio==1.2.0 -sqlalchemy==1.4.31 -starlette==0.17.1 -text-unidecode==1.3 -typing-extensions==3.10.0.2 -unidecode==1.3.2 -urllib3==1.26.9 -uvicorn==0.17.4 -win32-setctime==1.1.0; sys_platform == "win32" diff --git a/.framework/python/backend/runtime.txt b/.framework/python/backend/runtime.txt deleted file mode 100644 index c6f7782..0000000 --- a/.framework/python/backend/runtime.txt +++ /dev/null @@ -1 +0,0 @@ -python-3.9.13 diff --git a/.framework/python/backend/scripts/format b/.framework/python/backend/scripts/format deleted file mode 100755 index 64a9b14..0000000 --- a/.framework/python/backend/scripts/format +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -set -e - -isort --force-single-line-imports app tests -autoflake --recursive --remove-all-unused-imports --remove-unused-variables --in-place app tests -black app tests -isort app tests diff --git a/.framework/python/backend/scripts/heroku_release.sh b/.framework/python/backend/scripts/heroku_release.sh deleted file mode 100755 index 95d8fe5..0000000 --- a/.framework/python/backend/scripts/heroku_release.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -# -# Usage: bin/heroku_deploy - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -NO_COLOR='\033[0m' - -set -euo pipefail - -printf "\n⏳${YELLOW} [Release Phase]: Running schema migrations.${NO_COLOR}\n" -alembic upgrade head -printf "\n⏳${YELLOW} [Release Phase]: Seeding.${NO_COLOR}\n" -./seeds.sh -printf "\n🎉${GREEN} [Release Phase]: Database is up to date.${NO_COLOR}\n" diff --git a/.framework/python/backend/scripts/lint b/.framework/python/backend/scripts/lint deleted file mode 100755 index ea56cfe..0000000 --- a/.framework/python/backend/scripts/lint +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -x - - -flake8 app --exclude=app/db/migrations -mypy app - -black --check app --diff -isort --check-only app diff --git a/.framework/python/backend/scripts/test b/.framework/python/backend/scripts/test deleted file mode 100755 index 23f48d1..0000000 --- a/.framework/python/backend/scripts/test +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -x - -pytest --cov=app --cov=tests --cov-report=term-missing --cov-config=setup.cfg ${@} diff --git a/.framework/python/backend/scripts/test-cov-html b/.framework/python/backend/scripts/test-cov-html deleted file mode 100755 index de5f3b1..0000000 --- a/.framework/python/backend/scripts/test-cov-html +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -x - -bash scripts/test --cov-report=html ${@} diff --git a/.framework/python/backend/seeds.sh b/.framework/python/backend/seeds.sh deleted file mode 100755 index eab281e..0000000 --- a/.framework/python/backend/seeds.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -python3 ./app/db/seeds.py diff --git a/.framework/python/backend/setup.cfg b/.framework/python/backend/setup.cfg deleted file mode 100644 index c880ac5..0000000 --- a/.framework/python/backend/setup.cfg +++ /dev/null @@ -1,88 +0,0 @@ -[coverage:report] -precision = 2 -exclude_lines = - pragma: no cover - raise NotImplementedError - raise NotImplemented - -[coverage:run] -source = app -branch = True - -[mypy] -plugins = pydantic.mypy - -strict_optional = True -warn_redundant_casts = True -warn_unused_ignores = True -disallow_any_generics = True -check_untyped_defs = True - -disallow_untyped_defs = True - -[pydantic-mypy] -init_forbid_extra = True -init_typed = True -warn_required_dynamic_aliases = True -warn_untyped_fields = True - -[mypy-sqlalchemy.*] -ignore_missing_imports = True - -[mypy-alembic.*] -ignore_missing_imports = True - -[mypy-loguru.*] -ignore_missing_imports = True - -[mypy-asyncpg.*] -ignore_missing_imports = True - -[mypy-bcrypt.*] -ignore_missing_imports = True - -[mypy-passlib.*] -ignore_missing_imports = True - -[mypy-slugify.*] -ignore_missing_imports = True - -[mypy-pypika.*] -ignore_missing_imports = True - -[flake8] -format = wemake -max-line-length = 88 -per-file-ignores = - # ignore error on builtin names for TypedTable classes, since just mapper for SQL table - app/db/queries/tables.py: WPS125, - - # ignore black disabling in some places for queries building using pypika - app/db/repositories/*.py: E800, - - app/api/dependencies/authentication.py: WPS201, -ignore = - # common errors: - # FastAPI architecture requires a lot of functions calls as default arguments, so ignore it here. - B008, - # docs are missing in this project. - D, RST - - # WPS: 3xx - # IMO, but the obligation to specify the base class is redundant. - WPS306, - - # WPS: 4xx - # FastAPI architecture requires a lot of complex calls as default arguments, so ignore it here. - WPS404, - # again, FastAPI DI architecture involves a lot of nested functions as DI providers. - WPS430, - # used for pypika operations - WPS465, - - # WPS: 6xx - # pydantic defines models in dataclasses model style, but not supported by WPS. - WPS601, -no-accept-encodings = True -nested-classes-whitelist=Config -inline-quotes = double diff --git a/.framework/python/charts/.helmignore b/.framework/python/charts/.helmignore deleted file mode 100644 index 0e8a0eb..0000000 --- a/.framework/python/charts/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/.framework/python/charts/Chart.yaml b/.framework/python/charts/Chart.yaml deleted file mode 100644 index b2beb19..0000000 --- a/.framework/python/charts/Chart.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v2 -name: app -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "1.16.0" diff --git a/.framework/python/charts/templates/_helpers.yml b/.framework/python/charts/templates/_helpers.yml deleted file mode 100644 index 49515f2..0000000 --- a/.framework/python/charts/templates/_helpers.yml +++ /dev/null @@ -1,24 +0,0 @@ -{{- define "anythink-tenant.backendHost" -}} - https://{{- .Release.Namespace }}-api. - {{- if eq .Values.clusterEnv "staging" }} - {{- .Values.stagingBackendHost }} - {{- else }} - {{- .Values.productionBackendHost }} - {{- end }} -{{- end }} - -{{- define "anythink-tenant.backendRepository" -}} - {{- if eq .Values.clusterEnv "staging" }} - {{- .Values.backend.image.stagingRepository }} - {{- else }} - {{- .Values.backend.image.repository }} - {{- end }} -{{- end }} - -{{- define "anythink-tenant.frontendRepository" -}} - {{- if eq .Values.clusterEnv "staging" }} - {{- .Values.frontend.image.stagingRepository }} - {{- else }} - {{- .Values.frontend.image.repository }} - {{- end }} -{{- end }} diff --git a/.framework/python/charts/templates/anythink-backend-deployment.yaml b/.framework/python/charts/templates/anythink-backend-deployment.yaml deleted file mode 100644 index e0febba..0000000 --- a/.framework/python/charts/templates/anythink-backend-deployment.yaml +++ /dev/null @@ -1,74 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: {{ .Values.backend.serviceName }} - name: {{ .Values.backend.serviceName }} -spec: - replicas: {{ .Values.backend.replicaCount }} - selector: - matchLabels: - app: {{ .Values.backend.serviceName }} - strategy: - type: Recreate - template: - metadata: - labels: - app: {{ .Values.backend.serviceName }} - date: {{ now | unixEpoch | quote }} - spec: - containers: - - args: - - sh - - -c - - "poetry run uvicorn --host=0.0.0.0 --port={{ .Values.backend.containerPort }} app.main:app" - env: - - name: APP_ENV - value: dev - - name: SECRET_KEY - value: e6F9KvSDf4dyXj - - name: DEBUG - value: "True" - - name: DATABASE_URL - value: "{{ .Values.database.connectionProtocol }}{{ .Values.database.env.password }}:@{{ .Values.database.serviceName }}:{{ .Values.database.servicePort }}/{{ .Values.database.databaseName }}" - image: "{{ include "anythink-tenant.backendRepository" .}}:{{ .Values.backend.image.tag }}" - imagePullPolicy: {{ .Values.backend.image.pullPolicy }} - name: {{ .Values.backend.serviceName }} - ports: - - containerPort: {{ .Values.backend.containerPort }} - name: http - protocol: TCP - startupProbe: - httpGet: - path: /health - port: http - failureThreshold: 30 - periodSeconds: 10 - livenessProbe: - httpGet: - path: /health - port: http - readinessProbe: - httpGet: - path: /health - port: http - resources: - {{- toYaml .Values.backend.resources | nindent 12 }} - initContainers: - - command: - - sh - - -c - - "poetry run alembic upgrade head && ./seeds.sh" - env: - - name: APP_ENV - value: dev - - name: SECRET_KEY - value: secret - - name: DEBUG - value: "True" - - name: DATABASE_URL - value: "{{ .Values.database.connectionProtocol}}{{ .Values.database.env.password}}:@{{ .Values.database.serviceName }}:{{ .Values.database.servicePort }}/{{ .Values.database.databaseName }}" - image: "{{ include "anythink-tenant.backendRepository" .}}:{{ .Values.backend.image.tag }}" - imagePullPolicy: {{ .Values.backend.image.pullPolicy }} - name: db-migrations - restartPolicy: Always diff --git a/.framework/python/charts/templates/anythink-backend-service.yaml b/.framework/python/charts/templates/anythink-backend-service.yaml deleted file mode 100644 index 21bb516..0000000 --- a/.framework/python/charts/templates/anythink-backend-service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - creationTimestamp: null - labels: - app: {{ .Values.backend.serviceName }} - name: {{ .Values.backend.serviceName }} -spec: - ports: - - name: "{{ .Values.backend.containerPort }}" - port: {{ .Values.backend.containerPort }} - targetPort: {{ .Values.backend.containerPort }} - selector: - app: {{ .Values.backend.serviceName }} diff --git a/.framework/python/charts/templates/anythink-frontend-deployment.yaml b/.framework/python/charts/templates/anythink-frontend-deployment.yaml deleted file mode 100644 index f9be249..0000000 --- a/.framework/python/charts/templates/anythink-frontend-deployment.yaml +++ /dev/null @@ -1,55 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: {{ .Values.frontend.serviceName }} - name: {{ .Values.frontend.serviceName }} -spec: - replicas: {{ .Values.frontend.replicaCount }} - selector: - matchLabels: - app: {{ .Values.frontend.serviceName }} - strategy: - type: Recreate - template: - metadata: - labels: - app: {{ .Values.frontend.serviceName }} - date: {{ now | unixEpoch | quote }} - spec: - containers: - - args: - - sh - - -c - - yarn start - env: - - name: NODE_ENV - value: development - - name: PORT - value: "{{ .Values.frontend.containerPort }}" - - name: REACT_APP_BACKEND_URL - value: {{ include "anythink-tenant.backendHost" .}} - image: "{{ include "anythink-tenant.frontendRepository" .}}:{{ .Values.frontend.image.tag }}" - imagePullPolicy: {{ .Values.frontend.image.pullPolicy }} - name: {{ .Values.frontend.serviceName }} - ports: - - containerPort: {{ .Values.frontend.containerPort }} - name: http - protocol: TCP - startupProbe: - httpGet: - path: / - port: http - failureThreshold: 30 - periodSeconds: 10 - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {{- toYaml .Values.frontend.resources | nindent 12 }} - restartPolicy: Always diff --git a/.framework/python/charts/templates/anythink-frontend-service.yaml b/.framework/python/charts/templates/anythink-frontend-service.yaml deleted file mode 100644 index 217f8c5..0000000 --- a/.framework/python/charts/templates/anythink-frontend-service.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: {{ .Values.frontend.serviceName }} - name: {{ .Values.frontend.serviceName }} -spec: - ports: - - name: "{{ .Values.frontend.containerPort }}" - port: {{ .Values.frontend.containerPort }} - targetPort: {{ .Values.frontend.containerPort }} - selector: - app: {{ .Values.frontend.serviceName }} diff --git a/.framework/python/charts/templates/database-deployment.yaml b/.framework/python/charts/templates/database-deployment.yaml deleted file mode 100644 index 19752ee..0000000 --- a/.framework/python/charts/templates/database-deployment.yaml +++ /dev/null @@ -1,44 +0,0 @@ -{{- if .Values.database.deploy }} -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: {{ .Values.database.serviceName }} - name: {{ .Values.database.serviceName }} -spec: - replicas: 1 - selector: - matchLabels: - app: {{ .Values.database.serviceName }} - strategy: - type: Recreate - template: - metadata: - labels: - app: {{ .Values.database.serviceName }} - spec: - containers: - - image: "{{ .Values.database.image.repository }}:{{ .Values.database.image.tag }}" - name: {{ .Values.database.serviceName }} - env: - - name: POSTGRES_HOST_AUTH_METHOD - value: trust - - name: POSTGRES_USER - value: {{ .Values.database.env.userName }} - - name: POSTGRES_PASSWORD - value: {{ .Values.database.env.password }} - - name: POSTGRES_DB - value: {{ .Values.database.databaseName }} - imagePullPolicy: {{ .Values.database.image.pullPolicy }} - ports: - - containerPort: {{ .Values.database.containerPort }} - resources: {} - volumeMounts: - - mountPath: /data/db - name: {{ .Values.database.serviceName }}-0 - restartPolicy: Always - volumes: - - name: {{ .Values.database.serviceName }}-0 - persistentVolumeClaim: - claimName: {{ .Values.database.serviceName }}-0 -{{- end }} diff --git a/.framework/python/charts/templates/database-pvc.yaml b/.framework/python/charts/templates/database-pvc.yaml deleted file mode 100644 index 88517f3..0000000 --- a/.framework/python/charts/templates/database-pvc.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - labels: - app: {{ .Values.database.serviceName }}-0 - name: {{ .Values.database.serviceName }}-0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Mi diff --git a/.framework/python/charts/templates/database-service.yaml b/.framework/python/charts/templates/database-service.yaml deleted file mode 100644 index 80b47d3..0000000 --- a/.framework/python/charts/templates/database-service.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: {{ .Values.database.serviceName }} - name: {{ .Values.database.serviceName }} -spec: - ports: - - name: "{{ .Values.database.servicePort }}" - port: {{ .Values.database.servicePort }} - targetPort: {{ .Values.database.servicePort }} - selector: - app: {{ .Values.database.serviceName }} diff --git a/.framework/python/charts/values.yaml b/.framework/python/charts/values.yaml deleted file mode 100644 index 0464a04..0000000 --- a/.framework/python/charts/values.yaml +++ /dev/null @@ -1,70 +0,0 @@ -clusterEnv: "" -productionBackendHost: "prod.anythink.market" -stagingBackendHost: "staging.anythink.market" - -backend: - serviceName: anythink-backend - containerPort: 3000 - replicaCount: 1 - service: - type: ClusterIP - port: 80 - image: - repository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/anythink-backend - stagingRepository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/staging-anythink-backend - pullPolicy: Always - # Overrides the image tag whose default is the chart appVersion. - tag: "latest" - resources: - limits: - cpu: 100m - memory: 512Mi - requests: - cpu: 100m - memory: 128Mi - -frontend: - serviceName: anythink-frontend - containerPort: 3001 - replicaCount: 1 - service: - type: ClusterIP - port: 80 - image: - repository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/anythink-frontend - stagingRepository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/staging-anythink-frontend - pullPolicy: Always - # Overrides the image tag whose default is the chart appVersion. - tag: "latest" - resources: - limits: - cpu: 600m - memory: 768Mi - requests: - cpu: 100m - memory: 128Mi - -database: - deploy: true - connectionProtocol: postgresql:// - serviceName: postgres-python - containerPort: 5433 - servicePort: 5432 - databaseName: anythink-market - replicaCount: 1 - env: - password: postgres - service: - type: ClusterIP - port: 80 - image: - repository: postgres - pullPolicy: IfNotPresent - tag: "latest" - resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi diff --git a/.framework/python/docker-compose.yml b/.framework/python/docker-compose.yml deleted file mode 100644 index d483966..0000000 --- a/.framework/python/docker-compose.yml +++ /dev/null @@ -1,66 +0,0 @@ -services: - anythink-backend-python: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-backend-python:latest - container_name: anythink-backend-python - command: > - sh -c "cd backend && - poetry install && - poetry export -f "requirements.txt" --without-hashes --with-credentials > "requirements.txt" - /wait-for-it.sh postgres-python:5432 -q -t 60 && - poetry run alembic upgrade head && - poetry run gunicorn app.main:app --worker-class=uvicorn.workers.UvicornWorker --bind=0.0.0.0:3000 --workers=5 --reload" - working_dir: /usr/src - volumes: - - ./:/usr/src - ports: - - "3000:3000" - environment: - - APP_ENV=dev - - SECRET_KEY=secret - - DEBUG=True - - DATABASE_URL=postgresql://postgres:@postgres-python:5432/anythink-market - - GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN=${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN} - depends_on: - - "postgres-python" - - anythink-frontend-react: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-frontend-react:latest - container_name: anythink-frontend-react - command: sh -c "cd frontend && /wait-for-it.sh anythink-backend-python:3000 -t 120 --strict -- curl --head -X GET --retry 30 --retry-connrefused --retry-delay 1 anythink-backend-python:3000/api/ping && yarn start" - environment: - - NODE_ENV=development - - PORT=3001 - - REACT_APP_BACKEND_URL=${CODESPACE_BACKEND_URL:-http://localhost:3000} - - WDS_SOCKET_PORT=${CODESPACE_WDS_SOCKET_PORT:-3001} - working_dir: /usr/src - volumes: - - ./:/usr/src/ - - /usr/src/frontend/node_modules - ports: - - "3001:3001" - depends_on: - - "anythink-backend-python" - - postgres-python: - container_name: postgres-python - restart: on-failure - image: postgres - logging: - driver: none - environment: - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: postgres - POSTGRES_DB: anythink-market - volumes: - - ~/postgres/data:/data/db - ports: - - '5433:5432' - - anythink-ack: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-ack:latest - container_name: anythink-ack - environment: - - GITHUB_TOKEN=$GITHUB_TOKEN - - CODESPACE_NAME=$CODESPACE_NAME - depends_on: - - "anythink-frontend-react" diff --git a/.framework/rails/.devcontainer/devcontainer.json b/.framework/rails/.devcontainer/devcontainer.json deleted file mode 100644 index 171e2d2..0000000 --- a/.framework/rails/.devcontainer/devcontainer.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Anythink Development Container", - "image": "public.ecr.aws/v0a2l7y2/wilco/anythink-devcontainer:latest" -} diff --git a/.framework/rails/backend/.gitignore b/.framework/rails/backend/.gitignore deleted file mode 100644 index e8245b7..0000000 --- a/.framework/rails/backend/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# See https://help.github.com/items/ignoring-files for more about ignoring files. -# -# If you find yourself ignoring temporary files generated by your text editor -# or operating system, you probably want to add a global ignore instead: -# git config --global core.excludesfile '~/.gitignore_global' - -# Ignore bundler config. -/.bundle - -# Ignore the default SQLite database. -/db/*.sqlite3 -/db/*.sqlite3-journal - -# Ignore all logfiles and tempfiles. -/log/* -/tmp/* -!/log/.keep -!/tmp/.keep - -# Ignore uploaded files in development -/storage/* - -/node_modules -/yarn-error.log - -/public/assets -.byebug_history - -# Ignore master key for decrypting credentials and more. -/config/master.key diff --git a/.framework/rails/backend/.ruby-version b/.framework/rails/backend/.ruby-version deleted file mode 100644 index 460b6fd..0000000 --- a/.framework/rails/backend/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.7.5 \ No newline at end of file diff --git a/.framework/rails/backend/Dockerfile.aws b/.framework/rails/backend/Dockerfile.aws deleted file mode 100644 index 18fe3bb..0000000 --- a/.framework/rails/backend/Dockerfile.aws +++ /dev/null @@ -1,12 +0,0 @@ -FROM ruby:2.7.5 - -RUN apt-get update -qq && apt-get install -y build-essential nodejs - -WORKDIR /usr/src -COPY backend ./backend -COPY .wilco ./.wilco - -# Pre-install gems -WORKDIR /usr/src/backend -RUN bundle install - diff --git a/.framework/rails/backend/Gemfile b/.framework/rails/backend/Gemfile deleted file mode 100644 index 2eca1e4..0000000 --- a/.framework/rails/backend/Gemfile +++ /dev/null @@ -1,69 +0,0 @@ -source 'https://rubygems.org' -git_source(:github) { |repo| "https://github.com/#{repo}.git" } - -ruby '2.7.5' - -# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '~> 6.1.0' -# Use Puma as the app server -gem 'puma', '~> 3.11' - -# Faraday -gem 'faraday' - -# Use Uglifier as compressor for JavaScript assets -gem 'uglifier', '>= 1.3.0' -# See https://github.com/rails/execjs#readme for more supported runtimes -# gem 'mini_racer', platforms: :ruby - -# Use CoffeeScript for .coffee assets and views -gem 'coffee-rails', '~> 4.2' -# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks -gem 'turbolinks', '~> 5' -# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder -gem 'jbuilder', '~> 2.5' -# Use Redis adapter to run Action Cable in production -# gem 'redis', '~> 4.0' -# Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' - -# Use ActiveStorage variant -# gem 'mini_magick', '~> 4.8' - -# Use Capistrano for deployment -# gem 'capistrano-rails', group: :development - -gem 'devise' -gem 'rack-cors' -gem 'acts-as-taggable-on', '~> 8.1.0' -gem 'jwt' -gem "acts_as_follower", github: "tcocca/acts_as_follower" -gem 'pg' - -# Reduces boot times through caching; required in config/boot.rb -gem 'bootsnap', '>= 1.1.0', require: false - -group :development, :test do - # Call 'byebug' anywhere in the code to stop execution and get a debugger console - gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] -end - -group :development do - # Access an interactive console on exception pages or by calling 'console' anywhere in the code. - gem 'web-console', '>= 3.3.0' - gem 'listen', '>= 3.0.5', '< 3.2' - # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring - gem 'spring' - gem 'spring-watcher-listen', '~> 2.0.0' -end - -group :test do - # Adds support for Capybara system testing and selenium driver - gem 'capybara', '>= 2.15', '< 4.0' - gem 'selenium-webdriver' - # Easy installation and use of chromedriver to run system tests with Chrome - gem 'chromedriver-helper' -end - -# Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/.framework/rails/backend/Gemfile.lock b/.framework/rails/backend/Gemfile.lock deleted file mode 100644 index 9e838c7..0000000 --- a/.framework/rails/backend/Gemfile.lock +++ /dev/null @@ -1,258 +0,0 @@ -GIT - remote: https://github.com/tcocca/acts_as_follower.git - revision: c5ac7b9601c4af01eb4d9112330b27be4d694ecc - specs: - acts_as_follower (0.2.1) - activerecord (>= 4.0) - -GEM - remote: https://rubygems.org/ - specs: - actioncable (6.1.4.4) - actionpack (= 6.1.4.4) - activesupport (= 6.1.4.4) - nio4r (~> 2.0) - websocket-driver (>= 0.6.1) - actionmailbox (6.1.4.4) - actionpack (= 6.1.4.4) - activejob (= 6.1.4.4) - activerecord (= 6.1.4.4) - activestorage (= 6.1.4.4) - activesupport (= 6.1.4.4) - mail (>= 2.7.1) - actionmailer (6.1.4.4) - actionpack (= 6.1.4.4) - actionview (= 6.1.4.4) - activejob (= 6.1.4.4) - activesupport (= 6.1.4.4) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) - actionpack (6.1.4.4) - actionview (= 6.1.4.4) - activesupport (= 6.1.4.4) - rack (~> 2.0, >= 2.0.9) - rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.1.4.4) - actionpack (= 6.1.4.4) - activerecord (= 6.1.4.4) - activestorage (= 6.1.4.4) - activesupport (= 6.1.4.4) - nokogiri (>= 1.8.5) - actionview (6.1.4.4) - activesupport (= 6.1.4.4) - builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.1.4.4) - activesupport (= 6.1.4.4) - globalid (>= 0.3.6) - activemodel (6.1.4.4) - activesupport (= 6.1.4.4) - activerecord (6.1.4.4) - activemodel (= 6.1.4.4) - activesupport (= 6.1.4.4) - activestorage (6.1.4.4) - actionpack (= 6.1.4.4) - activejob (= 6.1.4.4) - activerecord (= 6.1.4.4) - activesupport (= 6.1.4.4) - marcel (~> 1.0.0) - mini_mime (>= 1.1.0) - activesupport (6.1.4.4) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 1.6, < 2) - minitest (>= 5.1) - tzinfo (~> 2.0) - zeitwerk (~> 2.3) - acts-as-taggable-on (8.1.0) - activerecord (>= 5.0, < 6.2) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) - archive-zip (0.12.0) - io-like (~> 0.3.0) - bcrypt (3.1.16) - bindex (0.8.1) - bootsnap (1.7.5) - msgpack (~> 1.0) - builder (3.2.4) - byebug (11.1.3) - capybara (3.35.3) - addressable - mini_mime (>= 0.1.3) - nokogiri (~> 1.8) - rack (>= 1.6.0) - rack-test (>= 0.6.3) - regexp_parser (>= 1.5, < 3.0) - xpath (~> 3.2) - childprocess (3.0.0) - chromedriver-helper (2.1.1) - archive-zip (~> 0.10) - nokogiri (~> 1.8) - coffee-rails (4.2.2) - coffee-script (>= 2.2.0) - railties (>= 4.0.0) - coffee-script (2.4.1) - coffee-script-source - execjs - coffee-script-source (1.12.2) - concurrent-ruby (1.1.9) - crass (1.0.6) - devise (4.8.1) - bcrypt (~> 3.0) - orm_adapter (~> 0.1) - railties (>= 4.1.0) - responders - warden (~> 1.2.3) - erubi (1.10.0) - execjs (2.7.0) - faraday (2.1.0) - faraday-net_http (~> 2.0) - ruby2_keywords (>= 0.0.4) - faraday-net_http (2.0.1) - ffi (1.15.0) - globalid (1.0.0) - activesupport (>= 5.0) - i18n (1.8.11) - concurrent-ruby (~> 1.0) - io-like (0.3.1) - jbuilder (2.11.2) - activesupport (>= 5.0.0) - jwt (1.5.6) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) - loofah (2.13.0) - crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) - mini_mime (>= 0.1.1) - marcel (1.0.2) - method_source (1.0.0) - mini_mime (1.1.0) - mini_portile2 (2.6.1) - minitest (5.15.0) - msgpack (1.4.2) - nio4r (2.5.8) - nokogiri (1.12.5) - mini_portile2 (~> 2.6.1) - racc (~> 1.4) - orm_adapter (0.5.0) - pg (1.2.3) - public_suffix (4.0.6) - puma (3.12.6) - racc (1.6.0) - rack (2.2.3) - rack-cors (1.1.1) - rack (>= 2.0.0) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rails (6.1.4.4) - actioncable (= 6.1.4.4) - actionmailbox (= 6.1.4.4) - actionmailer (= 6.1.4.4) - actionpack (= 6.1.4.4) - actiontext (= 6.1.4.4) - actionview (= 6.1.4.4) - activejob (= 6.1.4.4) - activemodel (= 6.1.4.4) - activerecord (= 6.1.4.4) - activestorage (= 6.1.4.4) - activesupport (= 6.1.4.4) - bundler (>= 1.15.0) - railties (= 6.1.4.4) - sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) - nokogiri (>= 1.6) - rails-html-sanitizer (1.4.2) - loofah (~> 2.3) - railties (6.1.4.4) - actionpack (= 6.1.4.4) - activesupport (= 6.1.4.4) - method_source - rake (>= 0.13) - thor (~> 1.0) - rake (13.0.6) - rb-fsevent (0.10.4) - rb-inotify (0.10.1) - ffi (~> 1.0) - regexp_parser (2.1.1) - responders (3.0.1) - actionpack (>= 5.0) - railties (>= 5.0) - ruby2_keywords (0.0.5) - ruby_dep (1.5.0) - rubyzip (2.3.0) - selenium-webdriver (3.142.7) - childprocess (>= 0.5, < 4.0) - rubyzip (>= 1.2.2) - spring (2.1.1) - spring-watcher-listen (2.0.1) - listen (>= 2.7, < 4.0) - spring (>= 1.2, < 3.0) - sprockets (4.0.2) - concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) - sprockets (>= 3.0.0) - thor (1.1.0) - turbolinks (5.2.1) - turbolinks-source (~> 5.2) - turbolinks-source (5.2.0) - tzinfo (2.0.4) - concurrent-ruby (~> 1.0) - uglifier (4.2.0) - execjs (>= 0.3.0, < 3) - warden (1.2.9) - rack (>= 2.0.9) - web-console (4.2.0) - actionview (>= 6.0.0) - activemodel (>= 6.0.0) - bindex (>= 0.4.0) - railties (>= 6.0.0) - websocket-driver (0.7.5) - websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.5) - xpath (3.2.0) - nokogiri (~> 1.8) - zeitwerk (2.5.2) - -PLATFORMS - ruby - -DEPENDENCIES - acts-as-taggable-on (~> 8.1.0) - acts_as_follower! - bootsnap (>= 1.1.0) - byebug - capybara (>= 2.15, < 4.0) - chromedriver-helper - coffee-rails (~> 4.2) - devise - faraday - jbuilder (~> 2.5) - jwt - listen (>= 3.0.5, < 3.2) - pg - puma (~> 3.11) - rack-cors - rails (~> 6.1.0) - selenium-webdriver - spring - spring-watcher-listen (~> 2.0.0) - turbolinks (~> 5) - tzinfo-data - uglifier (>= 1.3.0) - web-console (>= 3.3.0) - -RUBY VERSION - ruby 2.7.5p203 - -BUNDLED WITH - 2.1.4 diff --git a/.framework/rails/backend/README.md b/.framework/rails/backend/README.md deleted file mode 100644 index f056f30..0000000 --- a/.framework/rails/backend/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Anythink Market Backend - -The Anythink Market backend is Ruby web app written with [Ruby On Rails](https://rubyonrails.org/) - -## Dependencies - -- [acts_as_follower](https://github.com/tcocca/acts_as_follower) - For implementing followers/following -- [acts_as_taggable](https://github.com/mbleigh/acts-as-taggable-on) - For implementing tagging functionality -- [Devise](https://github.com/plataformatec/devise) - For implementing authentication -- [Jbuilder](https://github.com/rails/jbuilder) - Default JSON rendering gem that ships with Rails, used for making reusable templates for JSON output. -- [JWT](https://github.com/jwt/ruby-jwt) - For generating and validating JWTs for authentication - -## Folders - -- `app/models` - Contains the database models for the application where we can define methods, validations, queries, and relations to other models. -- `app/views` - Contains templates for generating the JSON output for the API -- `app/controllers` - Contains the controllers where requests are routed to their actions, where we find and manipulate our models and return them for the views to render. -- `config` - Contains configuration files for our Rails application and for our database, along with an `initializers` folder for scripts that get run on boot. -- `db` - Contains the migrations needed to create our database schema. diff --git a/.framework/rails/backend/Rakefile b/.framework/rails/backend/Rakefile deleted file mode 100644 index e85f913..0000000 --- a/.framework/rails/backend/Rakefile +++ /dev/null @@ -1,6 +0,0 @@ -# Add your own tasks in files placed in lib/tasks ending in .rake, -# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. - -require_relative 'config/application' - -Rails.application.load_tasks diff --git a/.framework/rails/backend/app/assets/config/manifest.js b/.framework/rails/backend/app/assets/config/manifest.js deleted file mode 100644 index b16e53d..0000000 --- a/.framework/rails/backend/app/assets/config/manifest.js +++ /dev/null @@ -1,3 +0,0 @@ -//= link_tree ../images -//= link_directory ../javascripts .js -//= link_directory ../stylesheets .css diff --git a/.framework/rails/backend/app/assets/images/.keep b/.framework/rails/backend/app/assets/images/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/rails/backend/app/assets/javascripts/.keep b/.framework/rails/backend/app/assets/javascripts/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/rails/backend/app/assets/stylesheets/.keep b/.framework/rails/backend/app/assets/stylesheets/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/rails/backend/app/channels/application_cable/channel.rb b/.framework/rails/backend/app/channels/application_cable/channel.rb deleted file mode 100644 index d672697..0000000 --- a/.framework/rails/backend/app/channels/application_cable/channel.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ApplicationCable - class Channel < ActionCable::Channel::Base - end -end diff --git a/.framework/rails/backend/app/channels/application_cable/connection.rb b/.framework/rails/backend/app/channels/application_cable/connection.rb deleted file mode 100644 index 0ff5442..0000000 --- a/.framework/rails/backend/app/channels/application_cable/connection.rb +++ /dev/null @@ -1,4 +0,0 @@ -module ApplicationCable - class Connection < ActionCable::Connection::Base - end -end diff --git a/.framework/rails/backend/app/controllers/application_controller.rb b/.framework/rails/backend/app/controllers/application_controller.rb deleted file mode 100644 index c45cfd2..0000000 --- a/.framework/rails/backend/app/controllers/application_controller.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -class ApplicationController < ActionController::API - include ActionController::HttpAuthentication::Token::ControllerMethods - - before_action :configure_permitted_parameters, if: :devise_controller? - before_action :authenticate_user - - def root - render plain: "API server is up and running, please use the frontend app to interact with the system" - end - - def health - render plain: 'OK' - end - - private - - def configure_permitted_parameters - devise_parameter_sanitizer.permit(:sign_up, keys: [:username]) - end - - def authenticate_user - return if request.headers['Authorization'].blank? - - authenticate_or_request_with_http_token do |token| - begin - jwt_payload = JWT.decode(token, Rails.application.secrets.secret_key_base).first - - @current_user_id = jwt_payload['id'] - rescue JWT::ExpiredSignature, JWT::VerificationError, JWT::DecodeError - head :unauthorized - end - end - end - - def authenticate_user!(_options = {}) - head :unauthorized unless signed_in? - end - - def current_user - @current_user ||= super || User.find(@current_user_id) - end - - def signed_in? - @current_user_id.present? - end -end diff --git a/.framework/rails/backend/app/controllers/comments_controller.rb b/.framework/rails/backend/app/controllers/comments_controller.rb deleted file mode 100644 index 6bd39e7..0000000 --- a/.framework/rails/backend/app/controllers/comments_controller.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -class CommentsController < ApplicationController - before_action :authenticate_user!, except: [:index] - before_action :find_item! - - def index - @comments = @item.comments.order(created_at: :desc) - end - - def create - @comment = @item.comments.new(comment_params) - @comment.user = current_user - - render json: { errors: @comment.errors }, status: :unprocessable_entity unless @comment.save - end - - def destroy - @comment = @item.comments.find(params[:id]) - - @comment.destroy - render json: {} - end - - private - - def comment_params - params.require(:comment).permit(:body) - end - - def find_item! - @item = Item.find_by!(slug: params[:item_slug]) - end -end diff --git a/.framework/rails/backend/app/controllers/concerns/.keep b/.framework/rails/backend/app/controllers/concerns/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/rails/backend/app/controllers/favorites_controller.rb b/.framework/rails/backend/app/controllers/favorites_controller.rb deleted file mode 100644 index 191d138..0000000 --- a/.framework/rails/backend/app/controllers/favorites_controller.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class FavoritesController < ApplicationController - before_action :authenticate_user! - before_action :find_item! - - def create - current_user.favorite(@item) - - render 'items/show' - end - - def destroy - current_user.unfavorite(@item) - - render 'items/show' - end - - private - - def find_item! - @item = Item.find_by!(slug: params[:item_slug]) - end -end diff --git a/.framework/rails/backend/app/controllers/follows_controller.rb b/.framework/rails/backend/app/controllers/follows_controller.rb deleted file mode 100644 index fa6f674..0000000 --- a/.framework/rails/backend/app/controllers/follows_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -class FollowsController < ApplicationController - before_action :authenticate_user! - - def create - @user = User.find_by!(username: params[:profile_username]) - - current_user.follow(@user) if current_user.id != @user.id - - render 'profiles/show' - end - - def destroy - @user = User.find_by!(username: params[:profile_username]) - - current_user.stop_following(@user) if current_user.id != @user.id - - render 'profiles/show' - end -end diff --git a/.framework/rails/backend/app/controllers/items_controller.rb b/.framework/rails/backend/app/controllers/items_controller.rb deleted file mode 100644 index 0e92499..0000000 --- a/.framework/rails/backend/app/controllers/items_controller.rb +++ /dev/null @@ -1,98 +0,0 @@ -# frozen_string_literal: true -require_relative "../../lib/event" -include Event - -class ItemsController < ApplicationController - before_action :authenticate_user!, except: %i[index show] - - def index - @items = Item.includes(:tags) - - @items = @items.tagged_with(params[:tag]) if params[:tag].present? - @items = @items.sellered_by(params[:seller]) if params[:seller].present? - @items = @items.favorited_by(params[:favorited]) if params[:favorited].present? - - @items_count = @items.count - - @items = @items.order(created_at: :desc).offset(params[:offset] || 0).limit(params[:limit] || 100) - - render json: { - items: @items.map { |item| - { - title: item.title, - slug: item.slug, - description: item.description, - image: item.image, - tagList: item.tags.map(&:name), - createdAt: item.created_at, - updatedAt: item.updated_at, - seller: { - username: item.user.username, - bio: item.user.bio, - image: item.user.image || 'https://static.productionready.io/images/smiley-cyrus.jpg', - following: signed_in? ? current_user.following?(item.user) : false, - }, - favorited: signed_in? ? current_user.favorited?(item) : false, - favoritesCount: item.favorites_count || 0 - } - }, - items_count: @items_count - } - end - - def feed - @items = Item.includes(:user).where(user: current_user.following_users) - - @items_count = @items.count - - @items = @items.order(created_at: :asc).offset(params[:offset] || 0).limit(params[:limit] || 20) - - render :index - end - - def create - @item = Item.new(item_params) - @item.user = current_user - - if @item.save - sendEvent("item_created", { item: item_params }) - render :show - else - render json: { errors: @item.errors }, status: :unprocessable_entity - end - end - - def show - @item = Item.find_by!(slug: params[:slug]) - end - - def update - @item = Item.find_by!(slug: params[:slug]) - - if @item.user_id == @current_user_id - @item.update(item_params) - - render :show - else - render json: { errors: { item: ['not owned by user'] } }, status: :forbidden - end - end - - def destroy - @item = Item.find_by!(slug: params[:slug]) - - if @item.user_id == @current_user_id - @item.destroy - - render json: {} - else - render json: { errors: { item: ['not owned by user'] } }, status: :forbidden - end - end - - private - - def item_params - params.require(:item).permit(:title, :description, :image, tag_list: []) - end -end diff --git a/.framework/rails/backend/app/controllers/ping_controller.rb b/.framework/rails/backend/app/controllers/ping_controller.rb deleted file mode 100644 index 4d72c28..0000000 --- a/.framework/rails/backend/app/controllers/ping_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -include Faraday -require_relative "../../lib/event" -include Event - -class PingController < ApplicationController - def index - response = sendEvent("ping", nil) - render json: response.body - end -end diff --git a/.framework/rails/backend/app/controllers/profiles_controller.rb b/.framework/rails/backend/app/controllers/profiles_controller.rb deleted file mode 100644 index 95898c8..0000000 --- a/.framework/rails/backend/app/controllers/profiles_controller.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class ProfilesController < ApplicationController - def show - @user = User.find_by(username: params[:username]) - end -end diff --git a/.framework/rails/backend/app/controllers/registrations_controller.rb b/.framework/rails/backend/app/controllers/registrations_controller.rb deleted file mode 100644 index 9a8f8e4..0000000 --- a/.framework/rails/backend/app/controllers/registrations_controller.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require_relative "../../lib/event" -include Event - -class RegistrationsController < Devise::RegistrationsController - def create - super - - if @user.persisted? - sendEvent("user_created", { username: @user.username }) - end - end -end diff --git a/.framework/rails/backend/app/controllers/sessions_controller.rb b/.framework/rails/backend/app/controllers/sessions_controller.rb deleted file mode 100644 index f2cdf07..0000000 --- a/.framework/rails/backend/app/controllers/sessions_controller.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -class SessionsController < Devise::SessionsController - def create - user = User.find_by(email: sign_in_params[:email]) - - if user && user.valid_password?(sign_in_params[:password]) - @current_user = user - else - render json: { errors: { 'email or password' => ['is invalid'] } }, status: :unprocessable_entity - end - end -end diff --git a/.framework/rails/backend/app/controllers/tags_controller.rb b/.framework/rails/backend/app/controllers/tags_controller.rb deleted file mode 100644 index 7d73a96..0000000 --- a/.framework/rails/backend/app/controllers/tags_controller.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class TagsController < ApplicationController - def index - render json: { tags: Item.tag_counts.most_used.map(&:name) } - end -end diff --git a/.framework/rails/backend/app/controllers/users_controller.rb b/.framework/rails/backend/app/controllers/users_controller.rb deleted file mode 100644 index c98f170..0000000 --- a/.framework/rails/backend/app/controllers/users_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -class UsersController < ApplicationController - before_action :authenticate_user! - - def show; end - - def update - if current_user.update(user_params) - render :show - else - render json: { errors: current_user.errors }, status: :unprocessable_entity - end - end - - private - - def user_params - params.require(:user).permit(:username, :email, :password, :bio, :image) - end -end diff --git a/.framework/rails/backend/app/helpers/application_helper.rb b/.framework/rails/backend/app/helpers/application_helper.rb deleted file mode 100644 index de6be79..0000000 --- a/.framework/rails/backend/app/helpers/application_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module ApplicationHelper -end diff --git a/.framework/rails/backend/app/jobs/application_job.rb b/.framework/rails/backend/app/jobs/application_job.rb deleted file mode 100644 index a009ace..0000000 --- a/.framework/rails/backend/app/jobs/application_job.rb +++ /dev/null @@ -1,2 +0,0 @@ -class ApplicationJob < ActiveJob::Base -end diff --git a/.framework/rails/backend/app/mailers/application_mailer.rb b/.framework/rails/backend/app/mailers/application_mailer.rb deleted file mode 100644 index 286b223..0000000 --- a/.framework/rails/backend/app/mailers/application_mailer.rb +++ /dev/null @@ -1,4 +0,0 @@ -class ApplicationMailer < ActionMailer::Base - default from: 'from@example.com' - layout 'mailer' -end diff --git a/.framework/rails/backend/app/models/application_record.rb b/.framework/rails/backend/app/models/application_record.rb deleted file mode 100644 index 10a4cba..0000000 --- a/.framework/rails/backend/app/models/application_record.rb +++ /dev/null @@ -1,3 +0,0 @@ -class ApplicationRecord < ActiveRecord::Base - self.abstract_class = true -end diff --git a/.framework/rails/backend/app/models/comment.rb b/.framework/rails/backend/app/models/comment.rb deleted file mode 100644 index 75d76cf..0000000 --- a/.framework/rails/backend/app/models/comment.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -class Comment < ApplicationRecord - belongs_to :user - belongs_to :item - - validates :body, presence: true, allow_blank: false -end diff --git a/.framework/rails/backend/app/models/concerns/.keep b/.framework/rails/backend/app/models/concerns/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/rails/backend/app/models/favorite.rb b/.framework/rails/backend/app/models/favorite.rb deleted file mode 100644 index afc2a4c..0000000 --- a/.framework/rails/backend/app/models/favorite.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -class Favorite < ApplicationRecord - belongs_to :user - belongs_to :item, counter_cache: true -end diff --git a/.framework/rails/backend/app/models/follow.rb b/.framework/rails/backend/app/models/follow.rb deleted file mode 100644 index 0746982..0000000 --- a/.framework/rails/backend/app/models/follow.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -class Follow < ApplicationRecord - extend ActsAsFollower::FollowerLib - extend ActsAsFollower::FollowScopes - - # NOTE: Follows belong to the "followable" interface, and also to followers - belongs_to :followable, polymorphic: true - belongs_to :follower, polymorphic: true - - def block! - update_attribute(:blocked, true) - end -end diff --git a/.framework/rails/backend/app/models/item.rb b/.framework/rails/backend/app/models/item.rb deleted file mode 100644 index b03d86e..0000000 --- a/.framework/rails/backend/app/models/item.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -class Item < ApplicationRecord - belongs_to :user - has_many :favorites, dependent: :destroy - has_many :comments, dependent: :destroy - - scope :sellered_by, ->(username) { where(user: User.where(username: username)) } - scope :favorited_by, ->(username) { joins(:favorites).where(favorites: { user: User.where(username: username) }) } - - acts_as_taggable - - validates :title, presence: true, allow_blank: false - validates :description, presence: true, allow_blank: false - validates :slug, uniqueness: true, exclusion: { in: ['feed'] } - - before_validation do - self.slug ||= "#{title.to_s.parameterize}-#{rand(36**6).to_s(36)}" - end -end diff --git a/.framework/rails/backend/app/models/user.rb b/.framework/rails/backend/app/models/user.rb deleted file mode 100644 index 1011b20..0000000 --- a/.framework/rails/backend/app/models/user.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -class User < ApplicationRecord - # Include default devise modules. Others available are: - # :confirmable, :lockable, :timeoutable and :omniauthable - devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :trackable, :validatable - - has_many :items, dependent: :destroy - has_many :favorites, dependent: :destroy - has_many :comments, dependent: :destroy - - acts_as_follower - acts_as_followable - - validates :username, uniqueness: { case_sensitive: true }, - format: { with: /\A[a-zA-Z0-9]+\z/ }, - presence: true, - allow_blank: false - - def generate_jwt - JWT.encode({ id: id, - exp: 60.days.from_now.to_i }, - Rails.application.secrets.secret_key_base) - end - - def favorite(item) - favorites.find_or_create_by(item: item) - end - - def unfavorite(item) - favorites.where(item: item).destroy_all - - item.reload - end - - def favorited?(item) - favorites.find_by(item_id: item.id).present? - end -end diff --git a/.framework/rails/backend/app/views/comments/_comment.json.jbuilder b/.framework/rails/backend/app/views/comments/_comment.json.jbuilder deleted file mode 100644 index 9fdf442..0000000 --- a/.framework/rails/backend/app/views/comments/_comment.json.jbuilder +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -json.call(comment, :id, :body) -json.createdAt comment.created_at -json.updatedAt comment.updated_at -json.seller comment.user, partial: 'profiles/profile', as: :user diff --git a/.framework/rails/backend/app/views/comments/create.json.jbuilder b/.framework/rails/backend/app/views/comments/create.json.jbuilder deleted file mode 100644 index e9773ab..0000000 --- a/.framework/rails/backend/app/views/comments/create.json.jbuilder +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -json.comment do |json| - json.partial! 'comments/comment', comment: @comment -end diff --git a/.framework/rails/backend/app/views/comments/index.json.jbuilder b/.framework/rails/backend/app/views/comments/index.json.jbuilder deleted file mode 100644 index 502bea2..0000000 --- a/.framework/rails/backend/app/views/comments/index.json.jbuilder +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -json.comments do |json| - json.array! @comments, partial: 'comments/comment', as: :comment -end diff --git a/.framework/rails/backend/app/views/devise/registrations/create.json.jbuilder b/.framework/rails/backend/app/views/devise/registrations/create.json.jbuilder deleted file mode 100644 index 60f1da9..0000000 --- a/.framework/rails/backend/app/views/devise/registrations/create.json.jbuilder +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -json.user do |json| - json.partial! 'users/user', user: current_user -end diff --git a/.framework/rails/backend/app/views/devise/sessions/create.json.jbuilder b/.framework/rails/backend/app/views/devise/sessions/create.json.jbuilder deleted file mode 100644 index 60f1da9..0000000 --- a/.framework/rails/backend/app/views/devise/sessions/create.json.jbuilder +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -json.user do |json| - json.partial! 'users/user', user: current_user -end diff --git a/.framework/rails/backend/app/views/items/_item.json.jbuilder b/.framework/rails/backend/app/views/items/_item.json.jbuilder deleted file mode 100644 index 183b449..0000000 --- a/.framework/rails/backend/app/views/items/_item.json.jbuilder +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -json.call(item, :title, :slug, :description, :image) -json.createdAt item.created_at -json.updatedAt item.updated_at -json.tagList item.tag_list -json.seller item.user, partial: 'profiles/profile', as: :user -json.favorited signed_in? ? current_user.favorited?(item) : false -json.favoritesCount item.favorites_count || 0 diff --git a/.framework/rails/backend/app/views/items/index.json.jbuilder b/.framework/rails/backend/app/views/items/index.json.jbuilder deleted file mode 100644 index 3d644dd..0000000 --- a/.framework/rails/backend/app/views/items/index.json.jbuilder +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -json.items do |json| - json.array! @items, partial: 'items/item', as: :item -end - -json.items_count @items_count diff --git a/.framework/rails/backend/app/views/items/show.json.jbuilder b/.framework/rails/backend/app/views/items/show.json.jbuilder deleted file mode 100644 index 2d84564..0000000 --- a/.framework/rails/backend/app/views/items/show.json.jbuilder +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -json.item do |json| - json.partial! 'items/item', item: @item -end diff --git a/.framework/rails/backend/app/views/profiles/_profile.json.jbuilder b/.framework/rails/backend/app/views/profiles/_profile.json.jbuilder deleted file mode 100644 index ded3482..0000000 --- a/.framework/rails/backend/app/views/profiles/_profile.json.jbuilder +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -json.call(user, :username, :bio) -json.image user.image || 'https://static.productionready.io/images/smiley-cyrus.jpg' -json.following signed_in? ? current_user.following?(user) : false diff --git a/.framework/rails/backend/app/views/profiles/show.json.jbuilder b/.framework/rails/backend/app/views/profiles/show.json.jbuilder deleted file mode 100644 index 887ba8e..0000000 --- a/.framework/rails/backend/app/views/profiles/show.json.jbuilder +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -json.profile do |json| - json.partial! 'profiles/profile', user: @user -end diff --git a/.framework/rails/backend/app/views/users/_user.json.jbuilder b/.framework/rails/backend/app/views/users/_user.json.jbuilder deleted file mode 100644 index a7ff346..0000000 --- a/.framework/rails/backend/app/views/users/_user.json.jbuilder +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -json.call(user, :id, :email, :username, :bio, :image) -json.token user.generate_jwt diff --git a/.framework/rails/backend/app/views/users/show.json.jbuilder b/.framework/rails/backend/app/views/users/show.json.jbuilder deleted file mode 100644 index 60f1da9..0000000 --- a/.framework/rails/backend/app/views/users/show.json.jbuilder +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -json.user do |json| - json.partial! 'users/user', user: current_user -end diff --git a/.framework/rails/backend/bin/bundle b/.framework/rails/backend/bin/bundle deleted file mode 100755 index f19acf5..0000000 --- a/.framework/rails/backend/bin/bundle +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -load Gem.bin_path('bundler', 'bundle') diff --git a/.framework/rails/backend/bin/heroku_release.sh b/.framework/rails/backend/bin/heroku_release.sh deleted file mode 100755 index 35fae48..0000000 --- a/.framework/rails/backend/bin/heroku_release.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# -# Usage: bin/heroku_deploy - -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[0;33m' -NO_COLOR='\033[0m' - -set -euo pipefail - -schema_version=$(bin/rails db:version | { grep "^Current version: [0-9]\\+$" || true; } | tr -s ' ' | cut -d ' ' -f3) - -if [ -z "$schema_version" ]; then - printf "\n⏳${YELLOW} [Release Phase]: Seeding db from scratch.${NO_COLOR}\n" - bin/rails db:init - bin/rails db:migrate - bin/rails db:seed -elif [ "$schema_version" -eq "0" ]; then - printf "\n⏳${YELLOW} [Release Phase]: Loading the database schema.${NO_COLOR}\n" - bin/rails db:schema:load - bin/rails db:seed -fi - -printf "\n🎉${GREEN} [Release Phase]: Database is up to date.${NO_COLOR}\n" \ No newline at end of file diff --git a/.framework/rails/backend/bin/rails b/.framework/rails/backend/bin/rails deleted file mode 100755 index 5badb2f..0000000 --- a/.framework/rails/backend/bin/rails +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -APP_PATH = File.expand_path('../config/application', __dir__) -require_relative '../config/boot' -require 'rails/commands' diff --git a/.framework/rails/backend/bin/rake b/.framework/rails/backend/bin/rake deleted file mode 100755 index d87d5f5..0000000 --- a/.framework/rails/backend/bin/rake +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -require_relative '../config/boot' -require 'rake' -Rake.application.run diff --git a/.framework/rails/backend/bin/setup b/.framework/rails/backend/bin/setup deleted file mode 100755 index 94fd4d7..0000000 --- a/.framework/rails/backend/bin/setup +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env ruby -require 'fileutils' -include FileUtils - -# path to your application root. -APP_ROOT = File.expand_path('..', __dir__) - -def system!(*args) - system(*args) || abort("\n== Command #{args} failed ==") -end - -chdir APP_ROOT do - # This script is a starting point to setup your application. - # Add necessary setup steps to this file. - - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - # Install JavaScript dependencies if using Yarn - # system('bin/yarn') - - # puts "\n== Copying sample files ==" - # unless File.exist?('config/database.yml') - # cp 'config/database.yml.sample', 'config/database.yml' - # end - - puts "\n== Preparing database ==" - system! 'bin/rails db:setup' - - puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' - - puts "\n== Restarting application server ==" - system! 'bin/rails restart' -end diff --git a/.framework/rails/backend/bin/spring b/.framework/rails/backend/bin/spring deleted file mode 100755 index d89ee49..0000000 --- a/.framework/rails/backend/bin/spring +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env ruby - -# This file loads Spring without using Bundler, in order to be fast. -# It gets overwritten when you run the `spring binstub` command. - -unless defined?(Spring) - require 'rubygems' - require 'bundler' - - lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) - spring = lockfile.specs.detect { |spec| spec.name == 'spring' } - if spring - Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path - gem 'spring', spring.version - require 'spring/binstub' - end -end diff --git a/.framework/rails/backend/bin/update b/.framework/rails/backend/bin/update deleted file mode 100755 index 58bfaed..0000000 --- a/.framework/rails/backend/bin/update +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env ruby -require 'fileutils' -include FileUtils - -# path to your application root. -APP_ROOT = File.expand_path('..', __dir__) - -def system!(*args) - system(*args) || abort("\n== Command #{args} failed ==") -end - -chdir APP_ROOT do - # This script is a way to update your development environment automatically. - # Add necessary update steps to this file. - - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - # Install JavaScript dependencies if using Yarn - # system('bin/yarn') - - puts "\n== Updating database ==" - system! 'bin/rails db:migrate' - - puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' - - puts "\n== Restarting application server ==" - system! 'bin/rails restart' -end diff --git a/.framework/rails/backend/bin/yarn b/.framework/rails/backend/bin/yarn deleted file mode 100755 index 460dd56..0000000 --- a/.framework/rails/backend/bin/yarn +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env ruby -APP_ROOT = File.expand_path('..', __dir__) -Dir.chdir(APP_ROOT) do - begin - exec "yarnpkg", *ARGV - rescue Errno::ENOENT - $stderr.puts "Yarn executable was not detected in the system." - $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" - exit 1 - end -end diff --git a/.framework/rails/backend/config.ru b/.framework/rails/backend/config.ru deleted file mode 100644 index f7ba0b5..0000000 --- a/.framework/rails/backend/config.ru +++ /dev/null @@ -1,5 +0,0 @@ -# This file is used by Rack-based servers to start the application. - -require_relative 'config/environment' - -run Rails.application diff --git a/.framework/rails/backend/config/application.rb b/.framework/rails/backend/config/application.rb deleted file mode 100644 index 75f6674..0000000 --- a/.framework/rails/backend/config/application.rb +++ /dev/null @@ -1,22 +0,0 @@ -require_relative 'boot' - -require 'rails/all' - -# Require the gems listed in Gemfile, including any gems -# you've limited to :test, :development, or :production. -Bundler.require(*Rails.groups) - -module AnythinkMarket - class Application < Rails::Application - # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 5.2 - config.api_only = true - - # Allow all hosts - config.hosts = nil - - config.to_prepare do - DeviseController.respond_to :html, :json - end - end -end diff --git a/.framework/rails/backend/config/boot.rb b/.framework/rails/backend/config/boot.rb deleted file mode 100644 index b9e460c..0000000 --- a/.framework/rails/backend/config/boot.rb +++ /dev/null @@ -1,4 +0,0 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) - -require 'bundler/setup' # Set up gems listed in the Gemfile. -require 'bootsnap/setup' # Speed up boot time by caching expensive operations. diff --git a/.framework/rails/backend/config/cable.yml b/.framework/rails/backend/config/cable.yml deleted file mode 100644 index 2dfc57c..0000000 --- a/.framework/rails/backend/config/cable.yml +++ /dev/null @@ -1,10 +0,0 @@ -development: - adapter: async - -test: - adapter: async - -production: - adapter: redis - url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> - channel_prefix: AnythinkMarket_production diff --git a/.framework/rails/backend/config/credentials.yml.enc b/.framework/rails/backend/config/credentials.yml.enc deleted file mode 100644 index 020476e..0000000 --- a/.framework/rails/backend/config/credentials.yml.enc +++ /dev/null @@ -1 +0,0 @@ -QAFQjUBh2bXf1g8q1zIUvSj39taFJML8JgK9OcL4vFBXG+N4F3lLlrG8N8Fjdzw34be/TgFRlrk5VkTYaf7jdcmL1OeFPK/Ny4/8In/PKg3LK319iY3ths+5rhBQzJqFxicmOg6VI/E6H1h3evxrKFEdcegN9F+Zf7U9bBmHgiWbD7FQemox/21EzFeyq+z3IiVlTB4OlEg9WT5GF5UPkeh2vT7LfCPLxb/yH9UntxcrJvkNzW3bgqXUhKzuT6dClpTpieHGJIEcQNGfMOhtu58SLX86/utPKBY+olX3cY7bwlReDYGxZECCruf5IL+UFg8bQM99TQZwwvlGpi+4HDHD4Rmkff4VGSgxNAOT7Uk4OiobK7mHbR+Wj78/48TEAGkGTr40AxDg4NVLegtLx81TZEejkH5tp1XF--BCh73RGeAwVePiSo--s/TsxgL/nFnzGqAYtDOVxA== \ No newline at end of file diff --git a/.framework/rails/backend/config/database.yml b/.framework/rails/backend/config/database.yml deleted file mode 100644 index ce77d59..0000000 --- a/.framework/rails/backend/config/database.yml +++ /dev/null @@ -1,21 +0,0 @@ -default: &default - adapter: postgresql - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 - url: <%= ENV['DATABASE_URL'] %> - -development: - <<: *default - encoding: unicode - pool: 5 - -test: - <<: *default - -production: - <<: *default - adapter: postgresql - encoding: unicode - pool: 5 - prepared_statements: false - advisory_locks: false diff --git a/.framework/rails/backend/config/environment.rb b/.framework/rails/backend/config/environment.rb deleted file mode 100644 index 426333b..0000000 --- a/.framework/rails/backend/config/environment.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Load the Rails application. -require_relative 'application' - -# Initialize the Rails application. -Rails.application.initialize! diff --git a/.framework/rails/backend/config/environments/development.rb b/.framework/rails/backend/config/environments/development.rb deleted file mode 100644 index 7e6af8d..0000000 --- a/.framework/rails/backend/config/environments/development.rb +++ /dev/null @@ -1,63 +0,0 @@ -Rails.application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. - config.cache_classes = false - - # Do not eager load code on boot. - config.eager_load = false - - # Show full error reports. - config.consider_all_requests_local = true - - # Enable/disable caching. By default caching is disabled. - # Run rails dev:cache to toggle caching. - if Rails.root.join('tmp', 'caching-dev.txt').exist? - config.action_controller.perform_caching = true - - config.cache_store = :memory_store - config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{2.days.to_i}" - } - else - config.action_controller.perform_caching = false - - config.cache_store = :null_store - end - - # Store uploaded files on the local file system (see config/storage.yml for options) - config.active_storage.service = :local - - # Don't care if the mailer can't send. - config.action_mailer.raise_delivery_errors = false - - config.action_mailer.perform_caching = false - - # Print deprecation notices to the Rails logger. - config.active_support.deprecation = :log - - # Raise an error on page load if there are pending migrations. - config.active_record.migration_error = :page_load - - # Highlight code that triggered database queries in logs. - config.active_record.verbose_query_logs = true - - # Debug mode disables concatenation and preprocessing of assets. - # This option may cause significant delays in view rendering with a large - # number of complex assets. - config.assets.debug = true - - # Suppress logger output for asset requests. - config.assets.quiet = true - - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true - - # Use an evented file watcher to asynchronously detect changes in source code, - # routes, locales, etc. This feature depends on the listen gem. - config.file_watcher = ActiveSupport::EventedFileUpdateChecker - - config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } -end diff --git a/.framework/rails/backend/config/environments/production.rb b/.framework/rails/backend/config/environments/production.rb deleted file mode 100644 index a9103c9..0000000 --- a/.framework/rails/backend/config/environments/production.rb +++ /dev/null @@ -1,93 +0,0 @@ -Rails.application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # Code is not reloaded between requests. - config.cache_classes = true - - # Eager load code on boot. This eager loads most of Rails and - # your application in memory, allowing both threaded web servers - # and those relying on copy on write to perform better. - # Rake tasks automatically ignore this option for performance. - config.eager_load = true - - # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false - config.action_controller.perform_caching = true - - # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] - # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). - # config.require_master_key = true - - # Disable serving static files from the `/public` folder by default since - # Apache or NGINX already handles this. - config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - - # Compress JavaScripts and CSS. - config.assets.js_compressor = :uglifier - - # Do not fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false - - # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb - - # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' - - # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX - - # Store uploaded files on the local file system (see config/storage.yml for options) - config.active_storage.service = :local - - # Mount Action Cable outside main process or domain - # config.action_cable.mount_path = nil - # config.action_cable.url = 'wss://example.com/cable' - # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] - - # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true - - # Use the lowest log level to ensure availability of diagnostic information - # when problems arise. - config.log_level = :debug - - # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] - - # Use a different cache store in production. - # config.cache_store = :mem_cache_store - - # Use a real queuing backend for Active Job (and separate queues per environment) - # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "AnythinkMarket_#{Rails.env}" - - config.action_mailer.perform_caching = false - - # Ignore bad email addresses and do not raise email delivery errors. - # Set this to true and configure the email server for immediate delivery to raise delivery errors. - # config.action_mailer.raise_delivery_errors = false - - # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation cannot be found). - config.i18n.fallbacks = true - - # Send deprecation notices to registered listeners. - config.active_support.deprecation = :notify - - # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new - - # Use a different logger for distributed setups. - # require 'syslog/logger' - # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') - - if ENV["RAILS_LOG_TO_STDOUT"].present? - logger = ActiveSupport::Logger.new(STDOUT) - logger.formatter = config.log_formatter - config.logger = ActiveSupport::TaggedLogging.new(logger) - end - - # Do not dump schema after migrations. - config.active_record.dump_schema_after_migration = false -end diff --git a/.framework/rails/backend/config/environments/test.rb b/.framework/rails/backend/config/environments/test.rb deleted file mode 100644 index 0a38fd3..0000000 --- a/.framework/rails/backend/config/environments/test.rb +++ /dev/null @@ -1,46 +0,0 @@ -Rails.application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # The test environment is used exclusively to run your application's - # test suite. You never need to work with it otherwise. Remember that - # your test database is "scratch space" for the test suite and is wiped - # and recreated between test runs. Don't rely on the data there! - config.cache_classes = true - - # Do not eager load code on boot. This avoids loading your whole application - # just for the purpose of running a single test. If you are using a tool that - # preloads Rails for running tests, you may have to set it to true. - config.eager_load = false - - # Configure public file server for tests with Cache-Control for performance. - config.public_file_server.enabled = true - config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{1.hour.to_i}" - } - - # Show full error reports and disable caching. - config.consider_all_requests_local = true - config.action_controller.perform_caching = false - - # Raise exceptions instead of rendering exception templates. - config.action_dispatch.show_exceptions = false - - # Disable request forgery protection in test environment. - config.action_controller.allow_forgery_protection = false - - # Store uploaded files on the local file system in a temporary directory - config.active_storage.service = :test - - config.action_mailer.perform_caching = false - - # Tell Action Mailer not to deliver emails to the real world. - # The :test delivery method accumulates sent emails in the - # ActionMailer::Base.deliveries array. - config.action_mailer.delivery_method = :test - - # Print deprecation notices to the stderr. - config.active_support.deprecation = :stderr - - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true -end diff --git a/.framework/rails/backend/config/initializers/application_controller_renderer.rb b/.framework/rails/backend/config/initializers/application_controller_renderer.rb deleted file mode 100644 index 89d2efa..0000000 --- a/.framework/rails/backend/config/initializers/application_controller_renderer.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# ActiveSupport::Reloader.to_prepare do -# ApplicationController.renderer.defaults.merge!( -# http_host: 'example.org', -# https: false -# ) -# end diff --git a/.framework/rails/backend/config/initializers/assets.rb b/.framework/rails/backend/config/initializers/assets.rb deleted file mode 100644 index 4b828e8..0000000 --- a/.framework/rails/backend/config/initializers/assets.rb +++ /dev/null @@ -1,14 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = '1.0' - -# Add additional assets to the asset load path. -# Rails.application.config.assets.paths << Emoji.images_path -# Add Yarn node_modules folder to the asset load path. -Rails.application.config.assets.paths << Rails.root.join('node_modules') - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in the app/assets -# folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) diff --git a/.framework/rails/backend/config/initializers/backtrace_silencers.rb b/.framework/rails/backend/config/initializers/backtrace_silencers.rb deleted file mode 100644 index 59385cd..0000000 --- a/.framework/rails/backend/config/initializers/backtrace_silencers.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. -# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } - -# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. -# Rails.backtrace_cleaner.remove_silencers! diff --git a/.framework/rails/backend/config/initializers/content_security_policy.rb b/.framework/rails/backend/config/initializers/content_security_policy.rb deleted file mode 100644 index d3bcaa5..0000000 --- a/.framework/rails/backend/config/initializers/content_security_policy.rb +++ /dev/null @@ -1,25 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Define an application-wide content security policy -# For further information see the following documentation -# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy - -# Rails.application.config.content_security_policy do |policy| -# policy.default_src :self, :https -# policy.font_src :self, :https, :data -# policy.img_src :self, :https, :data -# policy.object_src :none -# policy.script_src :self, :https -# policy.style_src :self, :https - -# # Specify URI for violation reports -# # policy.report_uri "/csp-violation-report-endpoint" -# end - -# If you are using UJS then enable automatic nonce generation -# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } - -# Report CSP violations to a specified URI -# For further information see the following documentation: -# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only -# Rails.application.config.content_security_policy_report_only = true diff --git a/.framework/rails/backend/config/initializers/cookies_serializer.rb b/.framework/rails/backend/config/initializers/cookies_serializer.rb deleted file mode 100644 index 5a6a32d..0000000 --- a/.framework/rails/backend/config/initializers/cookies_serializer.rb +++ /dev/null @@ -1,5 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Specify a serializer for the signed and encrypted cookie jars. -# Valid options are :json, :marshal, and :hybrid. -Rails.application.config.action_dispatch.cookies_serializer = :json diff --git a/.framework/rails/backend/config/initializers/cors.rb b/.framework/rails/backend/config/initializers/cors.rb deleted file mode 100644 index b745aba..0000000 --- a/.framework/rails/backend/config/initializers/cors.rb +++ /dev/null @@ -1,9 +0,0 @@ -Rails.application.config.middleware.insert_before 0, Rack::Cors, debug: true, logger: (-> { Rails.logger }) do - allow do - origins '*' - - resource '*', - headers: :any, - methods: [:get, :post, :put, :patch, :delete, :options, :head] - end -end diff --git a/.framework/rails/backend/config/initializers/devise.rb b/.framework/rails/backend/config/initializers/devise.rb deleted file mode 100644 index 002356b..0000000 --- a/.framework/rails/backend/config/initializers/devise.rb +++ /dev/null @@ -1,311 +0,0 @@ -# frozen_string_literal: true - -# Assuming you have not yet modified this file, each configuration option below -# is set to its default value. Note that some are commented out while others -# are not: uncommented lines are intended to protect your configuration from -# breaking changes in upgrades (i.e., in the event that future versions of -# Devise change the default values for those options). -# -# Use this hook to configure devise mailer, warden hooks and so forth. -# Many of these configuration options can be set straight in your model. -Devise.setup do |config| - # The secret key used by Devise. Devise uses this key to generate - # random tokens. Changing this key will render invalid all existing - # confirmation, reset password and unlock tokens in the database. - # Devise will use the `secret_key_base` as its `secret_key` - # by default. You can change it below and use your own secret key. - # config.secret_key = 'eca6e8be56768ce1455d5a43892b0db1144c4015b2417aac99712a9f7df34677e212942a0c4e8f8768b6b9458e0b453774e0c2a9ac3f600b5e78842e725be180' - - # ==> Controller configuration - # Configure the parent class to the devise controllers. - # config.parent_controller = 'DeviseController' - - # ==> Mailer Configuration - # Configure the e-mail address which will be shown in Devise::Mailer, - # note that it will be overwritten if you use your own mailer class - # with default "from" parameter. - config.mailer_sender = 'hello@anythink.family' - - # Configure the class responsible to send e-mails. - # config.mailer = 'Devise::Mailer' - - # Configure the parent class responsible to send e-mails. - # config.parent_mailer = 'ActionMailer::Base' - - # ==> ORM configuration - # Load and configure the ORM. Supports :active_record (default) and - # :mongoid (bson_ext recommended) by default. Other ORMs may be - # available as additional gems. - require 'devise/orm/active_record' - - # ==> Configuration for any authentication mechanism - # Configure which keys are used when authenticating a user. The default is - # just :email. You can configure it to use [:username, :subdomain], so for - # authenticating a user, both parameters are required. Remember that those - # parameters are used only when authenticating and not when retrieving from - # session. If you need permissions, you should implement that in a before filter. - # You can also supply a hash where the value is a boolean determining whether - # or not authentication should be aborted when the value is not present. - # config.authentication_keys = [:email] - - # Configure parameters from the request object used for authentication. Each entry - # given should be a request method and it will automatically be passed to the - # find_for_authentication method and considered in your model lookup. For instance, - # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. - # The same considerations mentioned for authentication_keys also apply to request_keys. - # config.request_keys = [] - - # Configure which authentication keys should be case-insensitive. - # These keys will be downcased upon creating or modifying a user and when used - # to authenticate or find a user. Default is :email. - config.case_insensitive_keys = [:email] - - # Configure which authentication keys should have whitespace stripped. - # These keys will have whitespace before and after removed upon creating or - # modifying a user and when used to authenticate or find a user. Default is :email. - config.strip_whitespace_keys = [:email] - - # Tell if authentication through request.params is enabled. True by default. - # It can be set to an array that will enable params authentication only for the - # given strategies, for example, `config.params_authenticatable = [:database]` will - # enable it only for database (email + password) authentication. - # config.params_authenticatable = true - - # Tell if authentication through HTTP Auth is enabled. False by default. - # It can be set to an array that will enable http authentication only for the - # given strategies, for example, `config.http_authenticatable = [:database]` will - # enable it only for database authentication. - # For API-only applications to support authentication "out-of-the-box", you will likely want to - # enable this with :database unless you are using a custom strategy. - # The supported strategies are: - # :database = Support basic authentication with authentication key + password - # config.http_authenticatable = false - - # If 401 status code should be returned for AJAX requests. True by default. - # config.http_authenticatable_on_xhr = true - - # The realm used in Http Basic Authentication. 'Application' by default. - # config.http_authentication_realm = 'Application' - - # It will change confirmation, password recovery and other workflows - # to behave the same regardless if the e-mail provided was right or wrong. - # Does not affect registerable. - # config.paranoid = true - - # By default Devise will store the user in session. You can skip storage for - # particular strategies by setting this option. - # Notice that if you are skipping storage for all authentication paths, you - # may want to disable generating routes to Devise's sessions controller by - # passing skip: :sessions to `devise_for` in your config/routes.rb - config.skip_session_storage = [:http_auth] - - # By default, Devise cleans up the CSRF token on authentication to - # avoid CSRF token fixation attacks. This means that, when using AJAX - # requests for sign in and sign up, you need to get a new CSRF token - # from the server. You can disable this option at your own risk. - # config.clean_up_csrf_token_on_authentication = true - - # When false, Devise will not attempt to reload routes on eager load. - # This can reduce the time taken to boot the app but if your application - # requires the Devise mappings to be loaded during boot time the application - # won't boot properly. - # config.reload_routes = true - - # ==> Configuration for :database_authenticatable - # For bcrypt, this is the cost for hashing the password and defaults to 12. If - # using other algorithms, it sets how many times you want the password to be hashed. - # The number of stretches used for generating the hashed password are stored - # with the hashed password. This allows you to change the stretches without - # invalidating existing passwords. - # - # Limiting the stretches to just one in testing will increase the performance of - # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use - # a value less than 10 in other environments. Note that, for bcrypt (the default - # algorithm), the cost increases exponentially with the number of stretches (e.g. - # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). - config.stretches = Rails.env.test? ? 1 : 12 - - # Set up a pepper to generate the hashed password. - # config.pepper = 'b278b1d480365dd815e58307e9c2cc160350cc20c62ca4a280be2a73b2a90f645a4f461a3278a45484073e0e3830cbbd1e133d7bbaf23e1dd9d580318b6c1876' - - # Send a notification to the original email when the user's email is changed. - # config.send_email_changed_notification = false - - # Send a notification email when the user's password is changed. - # config.send_password_change_notification = false - - # ==> Configuration for :confirmable - # A period that the user is allowed to access the website even without - # confirming their account. For instance, if set to 2.days, the user will be - # able to access the website for two days without confirming their account, - # access will be blocked just in the third day. - # You can also set it to nil, which will allow the user to access the website - # without confirming their account. - # Default is 0.days, meaning the user cannot access the website without - # confirming their account. - # config.allow_unconfirmed_access_for = 2.days - - # A period that the user is allowed to confirm their account before their - # token becomes invalid. For example, if set to 3.days, the user can confirm - # their account within 3 days after the mail was sent, but on the fourth day - # their account can't be confirmed with the token any more. - # Default is nil, meaning there is no restriction on how long a user can take - # before confirming their account. - # config.confirm_within = 3.days - - # If true, requires any email changes to be confirmed (exactly the same way as - # initial account confirmation) to be applied. Requires additional unconfirmed_email - # db field (see migrations). Until confirmed, new email is stored in - # unconfirmed_email column, and copied to email column on successful confirmation. - config.reconfirmable = true - - # Defines which key will be used when confirming an account - # config.confirmation_keys = [:email] - - # ==> Configuration for :rememberable - # The time the user will be remembered without asking for credentials again. - # config.remember_for = 2.weeks - - # Invalidates all the remember me tokens when the user signs out. - config.expire_all_remember_me_on_sign_out = true - - # If true, extends the user's remember period when remembered via cookie. - # config.extend_remember_period = false - - # Options to be passed to the created cookie. For instance, you can set - # secure: true in order to force SSL only cookies. - # config.rememberable_options = {} - - # ==> Configuration for :validatable - # Range for password length. - config.password_length = 6..128 - - # Email regex used to validate email formats. It simply asserts that - # one (and only one) @ exists in the given string. This is mainly - # to give user feedback and not to assert the e-mail validity. - config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ - - # ==> Configuration for :timeoutable - # The time you want to timeout the user session without activity. After this - # time the user will be asked for credentials again. Default is 30 minutes. - # config.timeout_in = 30.minutes - - # ==> Configuration for :lockable - # Defines which strategy will be used to lock an account. - # :failed_attempts = Locks an account after a number of failed attempts to sign in. - # :none = No lock strategy. You should handle locking by yourself. - # config.lock_strategy = :failed_attempts - - # Defines which key will be used when locking and unlocking an account - # config.unlock_keys = [:email] - - # Defines which strategy will be used to unlock an account. - # :email = Sends an unlock link to the user email - # :time = Re-enables login after a certain amount of time (see :unlock_in below) - # :both = Enables both strategies - # :none = No unlock strategy. You should handle unlocking by yourself. - # config.unlock_strategy = :both - - # Number of authentication tries before locking an account if lock_strategy - # is failed attempts. - # config.maximum_attempts = 20 - - # Time interval to unlock the account if :time is enabled as unlock_strategy. - # config.unlock_in = 1.hour - - # Warn on the last attempt before the account is locked. - # config.last_attempt_warning = true - - # ==> Configuration for :recoverable - # - # Defines which key will be used when recovering the password for an account - # config.reset_password_keys = [:email] - - # Time interval you can reset your password with a reset password key. - # Don't put a too small interval or your users won't have the time to - # change their passwords. - config.reset_password_within = 6.hours - - # When set to false, does not sign a user in automatically after their password is - # reset. Defaults to true, so a user is signed in automatically after a reset. - # config.sign_in_after_reset_password = true - - # ==> Configuration for :encryptable - # Allow you to use another hashing or encryption algorithm besides bcrypt (default). - # You can use :sha1, :sha512 or algorithms from others authentication tools as - # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20 - # for default behavior) and :restful_authentication_sha1 (then you should set - # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper). - # - # Require the `devise-encryptable` gem when using anything other than bcrypt - # config.encryptor = :sha512 - - # ==> Scopes configuration - # Turn scoped views on. Before rendering "sessions/new", it will first check for - # "users/sessions/new". It's turned off by default because it's slower if you - # are using only default views. - # config.scoped_views = false - - # Configure the default scope given to Warden. By default it's the first - # devise role declared in your routes (usually :user). - # config.default_scope = :user - - # Set this configuration to false if you want /users/sign_out to sign out - # only the current scope. By default, Devise signs out all scopes. - # config.sign_out_all_scopes = true - - # ==> Navigation configuration - # Lists the formats that should be treated as navigational. Formats like - # :html, should redirect to the sign in page when the user does not have - # access, but formats like :xml or :json, should return 401. - # - # If you have any extra navigational formats, like :iphone or :mobile, you - # should add them to the navigational formats lists. - # - # The "*/*" below is required to match Internet Explorer requests. - # config.navigational_formats = ['*/*', :html] - - # The default HTTP method used to sign out a resource. Default is :delete. - config.sign_out_via = :delete - - # ==> OmniAuth - # Add a new OmniAuth provider. Check the wiki for more information on setting - # up on your models and hooks. - # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' - - # ==> Warden configuration - # If you want to use other strategies, that are not supported by Devise, or - # change the failure app, you can configure them inside the config.warden block. - # - # config.warden do |manager| - # manager.intercept_401 = false - # manager.default_strategies(scope: :user).unshift :some_external_strategy - # end - - # ==> Mountable engine configurations - # When using Devise inside an engine, let's call it `MyEngine`, and this engine - # is mountable, there are some extra configurations to be taken into account. - # The following options are available, assuming the engine is mounted as: - # - # mount MyEngine, at: '/my_engine' - # - # The router that invoked `devise_for`, in the example above, would be: - # config.router_name = :my_engine - # - # When using OmniAuth, Devise cannot automatically set OmniAuth path, - # so you need to do it manually. For the users scope, it would be: - # config.omniauth_path_prefix = '/my_engine/users/auth' - - # ==> Turbolinks configuration - # If your app is using Turbolinks, Turbolinks::Controller needs to be included to make redirection work correctly: - # - # ActiveSupport.on_load(:devise_failure_app) do - # include Turbolinks::Controller - # end - - # ==> Configuration for :registerable - - # When set to false, does not sign a user in automatically after their password is - # changed. Defaults to true, so a user is signed in automatically after changing a password. - # config.sign_in_after_change_password = true -end diff --git a/.framework/rails/backend/config/initializers/filter_parameter_logging.rb b/.framework/rails/backend/config/initializers/filter_parameter_logging.rb deleted file mode 100644 index 4a994e1..0000000 --- a/.framework/rails/backend/config/initializers/filter_parameter_logging.rb +++ /dev/null @@ -1,4 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += [:password] diff --git a/.framework/rails/backend/config/initializers/inflections.rb b/.framework/rails/backend/config/initializers/inflections.rb deleted file mode 100644 index ac033bf..0000000 --- a/.framework/rails/backend/config/initializers/inflections.rb +++ /dev/null @@ -1,16 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Add new inflection rules using the following format. Inflections -# are locale specific, and you may define rules for as many different -# locales as you wish. All of these examples are active by default: -# ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.plural /^(ox)$/i, '\1en' -# inflect.singular /^(ox)en/i, '\1' -# inflect.irregular 'person', 'people' -# inflect.uncountable %w( fish sheep ) -# end - -# These inflection rules are supported but not enabled by default: -# ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.acronym 'RESTful' -# end diff --git a/.framework/rails/backend/config/initializers/json_param_key_transform.rb b/.framework/rails/backend/config/initializers/json_param_key_transform.rb deleted file mode 100644 index 7f88929..0000000 --- a/.framework/rails/backend/config/initializers/json_param_key_transform.rb +++ /dev/null @@ -1,17 +0,0 @@ -# File: config/initializers/json_param_key_transform.rb -# Transform JSON request param keys from JSON-conventional camelCase to -# Rails-conventional snake_case: -ActionDispatch::Request.parameter_parsers[:json] = lambda { |raw_post| - # Modified from action_dispatch/http/parameters.rb - data = ActiveSupport::JSON.decode(raw_post) - - # Transform camelCase param keys to snake_case - if data.is_a?(Array) - data.map { |item| item.deep_transform_keys!(&:underscore) } - else - data.deep_transform_keys!(&:underscore) - end - - # Return data - data.is_a?(Hash) ? data : { '_json': data } -} diff --git a/.framework/rails/backend/config/initializers/mime_types.rb b/.framework/rails/backend/config/initializers/mime_types.rb deleted file mode 100644 index dc18996..0000000 --- a/.framework/rails/backend/config/initializers/mime_types.rb +++ /dev/null @@ -1,4 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Add new mime types for use in respond_to blocks: -# Mime::Type.register "text/richtext", :rtf diff --git a/.framework/rails/backend/config/initializers/wrap_parameters.rb b/.framework/rails/backend/config/initializers/wrap_parameters.rb deleted file mode 100644 index bbfc396..0000000 --- a/.framework/rails/backend/config/initializers/wrap_parameters.rb +++ /dev/null @@ -1,14 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# This file contains settings for ActionController::ParamsWrapper which -# is enabled by default. - -# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. -ActiveSupport.on_load(:action_controller) do - wrap_parameters format: [:json] -end - -# To enable root element in JSON for ActiveRecord objects. -# ActiveSupport.on_load(:active_record) do -# self.include_root_in_json = true -# end diff --git a/.framework/rails/backend/config/locales/devise.en.yml b/.framework/rails/backend/config/locales/devise.en.yml deleted file mode 100644 index 260e1c4..0000000 --- a/.framework/rails/backend/config/locales/devise.en.yml +++ /dev/null @@ -1,65 +0,0 @@ -# Additional translations at https://github.com/heartcombo/devise/wiki/I18n - -en: - devise: - confirmations: - confirmed: "Your email address has been successfully confirmed." - send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." - failure: - already_authenticated: "You are already signed in." - inactive: "Your account is not activated yet." - invalid: "Invalid %{authentication_keys} or password." - locked: "Your account is locked." - last_attempt: "You have one more attempt before your account is locked." - not_found_in_database: "Invalid %{authentication_keys} or password." - timeout: "Your session expired. Please sign in again to continue." - unauthenticated: "You need to sign in or sign up before continuing." - unconfirmed: "You have to confirm your email address before continuing." - mailer: - confirmation_instructions: - subject: "Confirmation instructions" - reset_password_instructions: - subject: "Reset password instructions" - unlock_instructions: - subject: "Unlock instructions" - email_changed: - subject: "Email Changed" - password_change: - subject: "Password Changed" - omniauth_callbacks: - failure: "Could not authenticate you from %{kind} because \"%{reason}\"." - success: "Successfully authenticated from %{kind} account." - passwords: - no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." - send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." - send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." - updated: "Your password has been changed successfully. You are now signed in." - updated_not_active: "Your password has been changed successfully." - registrations: - destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." - signed_up: "Welcome! You have signed up successfully." - signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." - signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." - signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." - update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirmation link to confirm your new email address." - updated: "Your account has been updated successfully." - updated_but_not_signed_in: "Your account has been updated successfully, but since your password was changed, you need to sign in again." - sessions: - signed_in: "Signed in successfully." - signed_out: "Signed out successfully." - already_signed_out: "Signed out successfully." - unlocks: - send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." - send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." - unlocked: "Your account has been unlocked successfully. Please sign in to continue." - errors: - messages: - already_confirmed: "was already confirmed, please try signing in" - confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" - expired: "has expired, please request a new one" - not_found: "not found" - not_locked: "was not locked" - not_saved: - one: "1 error prohibited this %{resource} from being saved:" - other: "%{count} errors prohibited this %{resource} from being saved:" diff --git a/.framework/rails/backend/config/locales/en.yml b/.framework/rails/backend/config/locales/en.yml deleted file mode 100644 index decc5a8..0000000 --- a/.framework/rails/backend/config/locales/en.yml +++ /dev/null @@ -1,33 +0,0 @@ -# Files in the config/locales directory are used for internationalization -# and are automatically loaded by Rails. If you want to use locales other -# than English, add the necessary files in this directory. -# -# To use the locales, use `I18n.t`: -# -# I18n.t 'hello' -# -# In views, this is aliased to just `t`: -# -# <%= t('hello') %> -# -# To use a different locale, set it with `I18n.locale`: -# -# I18n.locale = :es -# -# This would use the information in config/locales/es.yml. -# -# The following keys must be escaped otherwise they will not be retrieved by -# the default I18n backend: -# -# true, false, on, off, yes, no -# -# Instead, surround them with single quotes. -# -# en: -# 'true': 'foo' -# -# To learn more, please read the Rails Internationalization guide -# available at http://guides.rubyonrails.org/i18n.html. - -en: - hello: "Hello world" diff --git a/.framework/rails/backend/config/locales/responders.en.yml b/.framework/rails/backend/config/locales/responders.en.yml deleted file mode 100644 index c3e147a..0000000 --- a/.framework/rails/backend/config/locales/responders.en.yml +++ /dev/null @@ -1,12 +0,0 @@ -en: - flash: - actions: - create: - notice: '%{resource_name} was successfully created.' - # alert: '%{resource_name} could not be created.' - update: - notice: '%{resource_name} was successfully updated.' - # alert: '%{resource_name} could not be updated.' - destroy: - notice: '%{resource_name} was successfully destroyed.' - alert: '%{resource_name} could not be destroyed.' diff --git a/.framework/rails/backend/config/puma.rb b/.framework/rails/backend/config/puma.rb deleted file mode 100644 index db6198c..0000000 --- a/.framework/rails/backend/config/puma.rb +++ /dev/null @@ -1,34 +0,0 @@ -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers: a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum; this matches the default thread size of Active Record. -# -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } -threads threads_count, threads_count - -# Specifies the `port` that Puma will listen on to receive requests; default is 3000. -# -port ENV.fetch("PORT") { 3000 } - -# Specifies the `environment` that Puma will run in. -# -environment ENV.fetch("RAILS_ENV") { "development" } - -# Specifies the number of `workers` to boot in clustered mode. -# Workers are forked webserver processes. If using threads and workers together -# the concurrency of the application would be max `threads` * `workers`. -# Workers do not work on JRuby or Windows (both of which do not support -# processes). -# -workers ENV.fetch("WEB_CONCURRENCY") { 3 } - -# Use the `preload_app!` method when specifying a `workers` number. -# This directive tells Puma to first boot the application and load code -# before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. -# -preload_app! - -# Allow puma to be restarted by `rails restart` command. -plugin :tmp_restart diff --git a/.framework/rails/backend/config/routes.rb b/.framework/rails/backend/config/routes.rb deleted file mode 100644 index b5ff26a..0000000 --- a/.framework/rails/backend/config/routes.rb +++ /dev/null @@ -1,27 +0,0 @@ - -Rails.application.routes.draw do - root to: "application#root" - - get '/health', to: 'application#health' - - scope :api, defaults: { format: :json } do - devise_for :users, controllers: { sessions: :sessions, registrations: :registrations }, - path_names: { sign_in: :login } - - resource :user, only: %i[show update] - - resources :profiles, param: :username, only: [:show] do - resource :follow, only: %i[create destroy] - end - - resources :items, param: :slug, except: %i[edit new] do - resource :favorite, only: %i[create destroy] - resources :comments, only: %i[create index destroy] - get :feed, on: :collection - end - - resources :tags, only: [:index] - - resources :ping, only: [:index] - end -end diff --git a/.framework/rails/backend/config/secrets.yml b/.framework/rails/backend/config/secrets.yml deleted file mode 100644 index 5482bab..0000000 --- a/.framework/rails/backend/config/secrets.yml +++ /dev/null @@ -1,26 +0,0 @@ -# Be sure to restart your server when you modify this file. - -# Your secret key is used for verifying the integrity of signed cookies. -# If you change this key, all old signed cookies will become invalid! - -# Make sure the secret is at least 30 characters and all random, -# no regular words or you'll be exposed to dictionary attacks. -# You can use `rails secret` to generate a secure secret key. - -# Make sure the secrets in this file are kept private -# if you're sharing your code publicly. - -# Shared secrets are available across all environments. - -shared: - -# Environmental secrets are only available for that specific environment. - -production: - secret_key_base: ceda0b32a04e63a9b997ec109c980c090bfa85cbdf26fc44288633dc9847b03ad8c460de5bcdb81e7cc0b0f158bb9b11a40105277a44e8d47e18e6639dd8d793 - -development: - secret_key_base: b4ec4bebe065b6d81ff57acc4c81464d6ce8e8488a67391e0d51f2707ebc4ea474d86fa6c52d22adefd141f7236d6f99da046f4945c4773e6ff5f100eb4f29ed - -test: - secret_key_base: 25c0c6b3cfc81e9357d13657f5545d3401da7af97a2be88c37c0d5afff8913f49fa2ba5dd75c21fedc8a974e27a8bd486f318510944899eb535175a5359a7277 diff --git a/.framework/rails/backend/config/secrets.yml.enc b/.framework/rails/backend/config/secrets.yml.enc deleted file mode 100644 index 534adbb..0000000 --- a/.framework/rails/backend/config/secrets.yml.enc +++ /dev/null @@ -1 +0,0 @@ -siBQfbvp2QIrFrw2Nb+S2ZJOKgQrXbV7tHsJWYafnxnruINEIo57G1+KVXIqfTFbvL95wh9R5dKoJj/7iJXk73KUv6VGg2AvmaWU9wEr9/nQsy2ojNAtg6F0T6ImsrzZchRDmycSPQkbQRBeqjDhuHqumSy8sqQOWeeIGM/4A99OxmCQIa2cAoQ09X38Og==--f7rABLaEoAVbrEWg--O15KZL5f10Z882jBsk4udA== \ No newline at end of file diff --git a/.framework/rails/backend/config/spring.rb b/.framework/rails/backend/config/spring.rb deleted file mode 100644 index 9fa7863..0000000 --- a/.framework/rails/backend/config/spring.rb +++ /dev/null @@ -1,6 +0,0 @@ -%w[ - .ruby-version - .rbenv-vars - tmp/restart.txt - tmp/caching-dev.txt -].each { |path| Spring.watch(path) } diff --git a/.framework/rails/backend/config/storage.yml b/.framework/rails/backend/config/storage.yml deleted file mode 100644 index d32f76e..0000000 --- a/.framework/rails/backend/config/storage.yml +++ /dev/null @@ -1,34 +0,0 @@ -test: - service: Disk - root: <%= Rails.root.join("tmp/storage") %> - -local: - service: Disk - root: <%= Rails.root.join("storage") %> - -# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) -# amazon: -# service: S3 -# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> -# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> -# region: us-east-1 -# bucket: your_own_bucket - -# Remember not to checkin your GCS keyfile to a repository -# google: -# service: GCS -# project: your_project -# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> -# bucket: your_own_bucket - -# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) -# microsoft: -# service: AzureStorage -# storage_account_name: your_account_name -# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> -# container: your_container_name - -# mirror: -# service: Mirror -# primary: local -# mirrors: [ amazon, google, microsoft ] diff --git a/.framework/rails/backend/db/migrate/20210412045707_devise_create_users.rb b/.framework/rails/backend/db/migrate/20210412045707_devise_create_users.rb deleted file mode 100644 index 78f4547..0000000 --- a/.framework/rails/backend/db/migrate/20210412045707_devise_create_users.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -class DeviseCreateUsers < ActiveRecord::Migration[4.2] - def change - create_table :users do |t| - ## Database authenticatable - t.string :email, null: false, default: '' - t.string :encrypted_password, null: false, default: '' - - ## Recoverable - t.string :reset_password_token - t.datetime :reset_password_sent_at - - ## Rememberable - t.datetime :remember_created_at - - ## Trackable - t.integer :sign_in_count, default: 0, null: false - t.datetime :current_sign_in_at - t.datetime :last_sign_in_at - t.string :current_sign_in_ip - t.string :last_sign_in_ip - - ## Confirmable - # t.string :confirmation_token - # t.datetime :confirmed_at - # t.datetime :confirmation_sent_at - # t.string :unconfirmed_email # Only if using reconfirmable - - ## Lockable - # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts - # t.string :unlock_token # Only if unlock strategy is :email or :both - # t.datetime :locked_at - - t.timestamps null: false - end - - add_index :users, :email, unique: true - add_index :users, :reset_password_token, unique: true - # add_index :users, :confirmation_token, unique: true - # add_index :users, :unlock_token, unique: true - end -end diff --git a/.framework/rails/backend/db/migrate/20210412045739_add_profile_fields_to_users.rb b/.framework/rails/backend/db/migrate/20210412045739_add_profile_fields_to_users.rb deleted file mode 100644 index 1230e71..0000000 --- a/.framework/rails/backend/db/migrate/20210412045739_add_profile_fields_to_users.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -class AddProfileFieldsToUsers < ActiveRecord::Migration[5.0] - def change - add_column :users, :username, :string - add_index :users, :username, unique: true - add_column :users, :image, :string - add_column :users, :bio, :text - end -end diff --git a/.framework/rails/backend/db/migrate/20210412052128_create_items.rb b/.framework/rails/backend/db/migrate/20210412052128_create_items.rb deleted file mode 100644 index 7b6cd3d..0000000 --- a/.framework/rails/backend/db/migrate/20210412052128_create_items.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -class CreateItems < ActiveRecord::Migration[5.0] - def change - create_table :items do |t| - t.string :title - t.string :slug - t.string :description - t.string :image - t.integer :favorites_count - t.references :user, index: true, foreign_key: true - - t.timestamps null: false - end - add_index :items, :slug, unique: true - end -end diff --git a/.framework/rails/backend/db/migrate/20210412054809_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb b/.framework/rails/backend/db/migrate/20210412054809_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb deleted file mode 100644 index 14d8c35..0000000 --- a/.framework/rails/backend/db/migrate/20210412054809_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -# This migration comes from acts_as_taggable_on_engine (originally 1) -class ActsAsTaggableOnMigration < ActiveRecord::Migration[5.0] - def self.up - create_table :tags do |t| - t.string :name - end - - create_table :taggings do |t| - t.references :tag - - # You should make sure that the column created is - # long enough to store the required class names. - t.references :taggable, polymorphic: true - t.references :tagger, polymorphic: true - - # Limit is created to prevent MySQL error on index - # length for MyISAM table type: http://bit.ly/vgW2Ql - t.string :context, limit: 128 - - t.datetime :created_at - end - - add_index :taggings, :tag_id unless index_exists?(:taggings, :tag_id) - add_index :taggings, %i[taggable_id taggable_type context] - end - - def self.down - drop_table :taggings - drop_table :tags - end -end diff --git a/.framework/rails/backend/db/migrate/20210412054810_add_missing_unique_indices.acts_as_taggable_on_engine.rb b/.framework/rails/backend/db/migrate/20210412054810_add_missing_unique_indices.acts_as_taggable_on_engine.rb deleted file mode 100644 index c0aa721..0000000 --- a/.framework/rails/backend/db/migrate/20210412054810_add_missing_unique_indices.acts_as_taggable_on_engine.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -# This migration comes from acts_as_taggable_on_engine (originally 2) -class AddMissingUniqueIndices < ActiveRecord::Migration[5.0] - def self.up - add_index :tags, :name, unique: true - - remove_index :taggings, :tag_id - remove_index :taggings, %i[taggable_id taggable_type context] - add_index :taggings, - %i[tag_id taggable_id taggable_type context tagger_id tagger_type], - unique: true, name: 'taggings_idx' - end - - def self.down - remove_index :tags, :name - - remove_index :taggings, name: 'taggings_idx' - add_index :taggings, :tag_id - add_index :taggings, %i[taggable_id taggable_type context] - end -end diff --git a/.framework/rails/backend/db/migrate/20210412054811_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb b/.framework/rails/backend/db/migrate/20210412054811_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb deleted file mode 100644 index 871f96f..0000000 --- a/.framework/rails/backend/db/migrate/20210412054811_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -# This migration comes from acts_as_taggable_on_engine (originally 3) -class AddTaggingsCounterCacheToTags < ActiveRecord::Migration[5.0] - def self.up - add_column :tags, :taggings_count, :integer, default: 0 - - ActsAsTaggableOn::Tag.reset_column_information - ActsAsTaggableOn::Tag.find_each do |tag| - ActsAsTaggableOn::Tag.reset_counters(tag.id, :taggings) - end - end - - def self.down - remove_column :tags, :taggings_count - end -end diff --git a/.framework/rails/backend/db/migrate/20210412054812_add_missing_taggable_index.acts_as_taggable_on_engine.rb b/.framework/rails/backend/db/migrate/20210412054812_add_missing_taggable_index.acts_as_taggable_on_engine.rb deleted file mode 100644 index 42fe2b7..0000000 --- a/.framework/rails/backend/db/migrate/20210412054812_add_missing_taggable_index.acts_as_taggable_on_engine.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -# This migration comes from acts_as_taggable_on_engine (originally 4) -class AddMissingTaggableIndex < ActiveRecord::Migration[5.0] - def self.up - add_index :taggings, %i[taggable_id taggable_type context] - end - - def self.down - remove_index :taggings, %i[taggable_id taggable_type context] - end -end diff --git a/.framework/rails/backend/db/migrate/20210412054813_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/.framework/rails/backend/db/migrate/20210412054813_change_collation_for_tag_names.acts_as_taggable_on_engine.rb deleted file mode 100644 index 29a11e3..0000000 --- a/.framework/rails/backend/db/migrate/20210412054813_change_collation_for_tag_names.acts_as_taggable_on_engine.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -# This migration comes from acts_as_taggable_on_engine (originally 5) -# This migration is added to circumvent issue #623 and have special characters -# work properly -class ChangeCollationForTagNames < ActiveRecord::Migration[5.0] - def up - if ActsAsTaggableOn::Utils.using_mysql? - execute('ALTER TABLE tags MODIFY name varchar(255) CHARACTER SET utf8 COLLATE utf8_bin;') - end - end -end diff --git a/.framework/rails/backend/db/migrate/20210412055201_create_favorites.rb b/.framework/rails/backend/db/migrate/20210412055201_create_favorites.rb deleted file mode 100644 index f9ef870..0000000 --- a/.framework/rails/backend/db/migrate/20210412055201_create_favorites.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -class CreateFavorites < ActiveRecord::Migration[5.0] - def change - create_table :favorites do |t| - t.references :user, index: true, foreign_key: true - t.references :item, index: true, foreign_key: true - - t.timestamps null: false - end - end -end diff --git a/.framework/rails/backend/db/migrate/20210412061113_create_comments.rb b/.framework/rails/backend/db/migrate/20210412061113_create_comments.rb deleted file mode 100644 index 12ccb33..0000000 --- a/.framework/rails/backend/db/migrate/20210412061113_create_comments.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -class CreateComments < ActiveRecord::Migration[5.0] - def change - create_table :comments do |t| - t.text :body - t.references :user, index: true, foreign_key: true - t.references :item, index: true, foreign_key: true - - t.timestamps null: false - end - end -end diff --git a/.framework/rails/backend/db/migrate/20210412061614_acts_as_follower_migration.rb b/.framework/rails/backend/db/migrate/20210412061614_acts_as_follower_migration.rb deleted file mode 100644 index 3fd715a..0000000 --- a/.framework/rails/backend/db/migrate/20210412061614_acts_as_follower_migration.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -class ActsAsFollowerMigration < ActiveRecord::Migration[5.0] - def self.up - create_table :follows, force: true do |t| - t.references :followable, polymorphic: true, null: false - t.references :follower, polymorphic: true, null: false - t.boolean :blocked, default: false, null: false - t.timestamps - end - - add_index :follows, %w[follower_id follower_type], name: 'fk_follows' - add_index :follows, %w[followable_id followable_type], name: 'fk_followables' - end - - def self.down - drop_table :follows - end -end diff --git a/.framework/rails/backend/db/schema.rb b/.framework/rails/backend/db/schema.rb deleted file mode 100644 index 44d2262..0000000 --- a/.framework/rails/backend/db/schema.rb +++ /dev/null @@ -1,110 +0,0 @@ -# This file is auto-generated from the current state of the database. Instead -# of editing this file, please use the migrations feature of Active Record to -# incrementally modify your database, and then regenerate this schema definition. -# -# This file is the source Rails uses to define your schema when running `bin/rails -# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to -# be faster and is potentially less error prone than running all of your -# migrations from scratch. Old migrations may fail to apply correctly if those -# migrations use external dependencies or application code. -# -# It's strongly recommended that you check this file into your version control system. - -ActiveRecord::Schema.define(version: 2021_04_12_061614) do - - # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" - - create_table "comments", id: :serial, force: :cascade do |t| - t.text "body" - t.integer "user_id" - t.integer "item_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["item_id"], name: "index_comments_on_item_id" - t.index ["user_id"], name: "index_comments_on_user_id" - end - - create_table "favorites", id: :serial, force: :cascade do |t| - t.integer "user_id" - t.integer "item_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["item_id"], name: "index_favorites_on_item_id" - t.index ["user_id"], name: "index_favorites_on_user_id" - end - - create_table "follows", id: :serial, force: :cascade do |t| - t.string "followable_type", null: false - t.integer "followable_id", null: false - t.string "follower_type", null: false - t.integer "follower_id", null: false - t.boolean "blocked", default: false, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["followable_id", "followable_type"], name: "fk_followables" - t.index ["followable_type", "followable_id"], name: "index_follows_on_followable" - t.index ["follower_id", "follower_type"], name: "fk_follows" - t.index ["follower_type", "follower_id"], name: "index_follows_on_follower" - end - - create_table "items", id: :serial, force: :cascade do |t| - t.string "title" - t.string "slug" - t.string "description" - t.string "image" - t.integer "favorites_count" - t.integer "user_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["slug"], name: "index_items_on_slug", unique: true - t.index ["user_id"], name: "index_items_on_user_id" - end - - create_table "taggings", id: :serial, force: :cascade do |t| - t.integer "tag_id" - t.string "taggable_type" - t.integer "taggable_id" - t.string "tagger_type" - t.integer "tagger_id" - t.string "context", limit: 128 - t.datetime "created_at" - t.index ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true - t.index ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context" - t.index ["taggable_type", "taggable_id"], name: "index_taggings_on_taggable" - t.index ["tagger_type", "tagger_id"], name: "index_taggings_on_tagger" - end - - create_table "tags", id: :serial, force: :cascade do |t| - t.string "name" - t.integer "taggings_count", default: 0 - t.index ["name"], name: "index_tags_on_name", unique: true - end - - create_table "users", id: :serial, force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" - t.datetime "reset_password_sent_at" - t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false - t.datetime "current_sign_in_at" - t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "username" - t.string "image" - t.text "bio" - t.index ["email"], name: "index_users_on_email", unique: true - t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true - t.index ["username"], name: "index_users_on_username", unique: true - end - - add_foreign_key "comments", "items" - add_foreign_key "comments", "users" - add_foreign_key "favorites", "items" - add_foreign_key "favorites", "users" - add_foreign_key "items", "users" -end diff --git a/.framework/rails/backend/db/seeds.rb b/.framework/rails/backend/db/seeds.rb deleted file mode 100644 index 1beea2a..0000000 --- a/.framework/rails/backend/db/seeds.rb +++ /dev/null @@ -1,7 +0,0 @@ -# This file should contain all the record creation needed to seed the database with its default values. -# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). -# -# Examples: -# -# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) -# Character.create(name: 'Luke', movie: movies.first) diff --git a/.framework/rails/backend/lib/assets/.keep b/.framework/rails/backend/lib/assets/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/rails/backend/lib/event.rb b/.framework/rails/backend/lib/event.rb deleted file mode 100644 index d548df7..0000000 --- a/.framework/rails/backend/lib/event.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Event - def sendEvent(eventName, metadata) - wilcoId = ENV['WILCO_ID'] || File.read(Rails.root.join("../.wilco")) - baseUrl = ENV['ENGINE_BASE_URL'] || "https://engine.wilco.gg" - conn = Faraday.new( - url: baseUrl + "/users/#{wilcoId}/", - headers: {'Content-Type' => 'application/json'} - ) - begin - response = conn.post('event') do |req| - req.headers['Content-Type'] = 'application/json' - req.body = { event: eventName, metadata: metadata}.to_json - end - rescue Exception => e - puts 'failed to send event #{wilcoId} to Wilco engine' - end - response - end -end diff --git a/.framework/rails/backend/lib/tasks/.keep b/.framework/rails/backend/lib/tasks/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/rails/backend/log/.keep b/.framework/rails/backend/log/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/rails/backend/public/404.html b/.framework/rails/backend/public/404.html deleted file mode 100644 index b612547..0000000 --- a/.framework/rails/backend/public/404.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - The page you were looking for doesn't exist (404) - - - - - - -
-
-

The page you were looking for doesn't exist.

-

You may have mistyped the address or the page may have moved.

-
-

If you are the application owner check the logs for more information.

-
- - diff --git a/.framework/rails/backend/public/422.html b/.framework/rails/backend/public/422.html deleted file mode 100644 index a21f82b..0000000 --- a/.framework/rails/backend/public/422.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - The change you wanted was rejected (422) - - - - - - -
-
-

The change you wanted was rejected.

-

Maybe you tried to change something you didn't have access to.

-
-

If you are the application owner check the logs for more information.

-
- - diff --git a/.framework/rails/backend/public/500.html b/.framework/rails/backend/public/500.html deleted file mode 100644 index 061abc5..0000000 --- a/.framework/rails/backend/public/500.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - We're sorry, but something went wrong (500) - - - - - - -
-
-

We're sorry, but something went wrong.

-
-

If you are the application owner check the logs for more information.

-
- - diff --git a/.framework/rails/backend/public/favicon.ico b/.framework/rails/backend/public/favicon.ico deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/rails/backend/public/robots.txt b/.framework/rails/backend/public/robots.txt deleted file mode 100644 index 37b576a..0000000 --- a/.framework/rails/backend/public/robots.txt +++ /dev/null @@ -1 +0,0 @@ -# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/.framework/rails/backend/seeds.sh b/.framework/rails/backend/seeds.sh deleted file mode 100755 index 8c1a5b6..0000000 --- a/.framework/rails/backend/seeds.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -bin/rails db:seed diff --git a/.framework/rails/backend/start.sh b/.framework/rails/backend/start.sh deleted file mode 100755 index bc5a109..0000000 --- a/.framework/rails/backend/start.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -bundle exec rails s diff --git a/.framework/rails/backend/start_rails.sh b/.framework/rails/backend/start_rails.sh deleted file mode 100755 index eec24c7..0000000 --- a/.framework/rails/backend/start_rails.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -rm -f /tmp/server.pid -bin/rails db:migrate -bin/rails s -b 0.0.0.0 --pid /tmp/server.pid diff --git a/.framework/rails/backend/vendor/.keep b/.framework/rails/backend/vendor/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/.framework/rails/charts/.helmignore b/.framework/rails/charts/.helmignore deleted file mode 100644 index 0e8a0eb..0000000 --- a/.framework/rails/charts/.helmignore +++ /dev/null @@ -1,23 +0,0 @@ -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*.orig -*~ -# Various IDEs -.project -.idea/ -*.tmproj -.vscode/ diff --git a/.framework/rails/charts/Chart.yaml b/.framework/rails/charts/Chart.yaml deleted file mode 100644 index b2beb19..0000000 --- a/.framework/rails/charts/Chart.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v2 -name: app -description: A Helm chart for Kubernetes - -# A chart can be either an 'application' or a 'library' chart. -# -# Application charts are a collection of templates that can be packaged into versioned archives -# to be deployed. -# -# Library charts provide useful utilities or functions for the chart developer. They're included as -# a dependency of application charts to inject those utilities and functions into the rendering -# pipeline. Library charts do not define any templates and therefore cannot be deployed. -type: application - -# This is the chart version. This version number should be incremented each time you make changes -# to the chart and its templates, including the app version. -# Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 - -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "1.16.0" diff --git a/.framework/rails/charts/templates/_helpers.yml b/.framework/rails/charts/templates/_helpers.yml deleted file mode 100644 index 49515f2..0000000 --- a/.framework/rails/charts/templates/_helpers.yml +++ /dev/null @@ -1,24 +0,0 @@ -{{- define "anythink-tenant.backendHost" -}} - https://{{- .Release.Namespace }}-api. - {{- if eq .Values.clusterEnv "staging" }} - {{- .Values.stagingBackendHost }} - {{- else }} - {{- .Values.productionBackendHost }} - {{- end }} -{{- end }} - -{{- define "anythink-tenant.backendRepository" -}} - {{- if eq .Values.clusterEnv "staging" }} - {{- .Values.backend.image.stagingRepository }} - {{- else }} - {{- .Values.backend.image.repository }} - {{- end }} -{{- end }} - -{{- define "anythink-tenant.frontendRepository" -}} - {{- if eq .Values.clusterEnv "staging" }} - {{- .Values.frontend.image.stagingRepository }} - {{- else }} - {{- .Values.frontend.image.repository }} - {{- end }} -{{- end }} diff --git a/.framework/rails/charts/templates/anythink-backend-deployment.yaml b/.framework/rails/charts/templates/anythink-backend-deployment.yaml deleted file mode 100644 index a71f3a7..0000000 --- a/.framework/rails/charts/templates/anythink-backend-deployment.yaml +++ /dev/null @@ -1,70 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: {{ .Values.backend.serviceName }} - name: {{ .Values.backend.serviceName }} -spec: - replicas: {{ .Values.backend.replicaCount }} - selector: - matchLabels: - app: {{ .Values.backend.serviceName }} - strategy: - type: Recreate - template: - metadata: - labels: - app: {{ .Values.backend.serviceName }} - date: {{ now | unixEpoch | quote }} - spec: - containers: - - args: - - sh - - -c - - "rm -f /tmp/server.pid && exec rails s -b 0.0.0.0 --pid /tmp/server.pid" - env: - - name: DATABASE_URL - value: "{{ .Values.database.connectionProtocol}}{{ .Values.database.env.userName}}:@{{ .Values.database.serviceName }}:{{ .Values.database.servicePort }}" - - name: RAILS_ENV - value: development - - name: PORT - value: "{{ .Values.backend.containerPort }}" - image: "{{ include "anythink-tenant.backendRepository" .}}:{{ .Values.backend.image.tag }}" - imagePullPolicy: {{ .Values.backend.image.pullPolicy }} - name: {{ .Values.backend.serviceName }} - ports: - - containerPort: {{ .Values.backend.containerPort }} - name: http - protocol: TCP - startupProbe: - httpGet: - path: /health - port: http - failureThreshold: 30 - periodSeconds: 10 - livenessProbe: - httpGet: - path: /health - port: http - readinessProbe: - httpGet: - path: /health - port: http - resources: - {{- toYaml .Values.backend.resources | nindent 12 }} - initContainers: - - command: - - sh - - -c - - "rails db:migrate && ./seeds.sh" - env: - - name: DATABASE_URL - value: "{{ .Values.database.connectionProtocol}}{{ .Values.database.env.userName}}:@{{ .Values.database.serviceName }}:{{ .Values.database.servicePort }}" - - name: RAILS_ENV - value: development - - name: PORT - value: "{{ .Values.backend.containerPort }}" - image: "{{ include "anythink-tenant.backendRepository" .}}:{{ .Values.backend.image.tag }}" - imagePullPolicy: {{ .Values.backend.image.pullPolicy }} - name: db-migrations - restartPolicy: Always diff --git a/.framework/rails/charts/templates/anythink-backend-service.yaml b/.framework/rails/charts/templates/anythink-backend-service.yaml deleted file mode 100644 index 21bb516..0000000 --- a/.framework/rails/charts/templates/anythink-backend-service.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - creationTimestamp: null - labels: - app: {{ .Values.backend.serviceName }} - name: {{ .Values.backend.serviceName }} -spec: - ports: - - name: "{{ .Values.backend.containerPort }}" - port: {{ .Values.backend.containerPort }} - targetPort: {{ .Values.backend.containerPort }} - selector: - app: {{ .Values.backend.serviceName }} diff --git a/.framework/rails/charts/templates/anythink-frontend-deployment.yaml b/.framework/rails/charts/templates/anythink-frontend-deployment.yaml deleted file mode 100644 index f9be249..0000000 --- a/.framework/rails/charts/templates/anythink-frontend-deployment.yaml +++ /dev/null @@ -1,55 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: {{ .Values.frontend.serviceName }} - name: {{ .Values.frontend.serviceName }} -spec: - replicas: {{ .Values.frontend.replicaCount }} - selector: - matchLabels: - app: {{ .Values.frontend.serviceName }} - strategy: - type: Recreate - template: - metadata: - labels: - app: {{ .Values.frontend.serviceName }} - date: {{ now | unixEpoch | quote }} - spec: - containers: - - args: - - sh - - -c - - yarn start - env: - - name: NODE_ENV - value: development - - name: PORT - value: "{{ .Values.frontend.containerPort }}" - - name: REACT_APP_BACKEND_URL - value: {{ include "anythink-tenant.backendHost" .}} - image: "{{ include "anythink-tenant.frontendRepository" .}}:{{ .Values.frontend.image.tag }}" - imagePullPolicy: {{ .Values.frontend.image.pullPolicy }} - name: {{ .Values.frontend.serviceName }} - ports: - - containerPort: {{ .Values.frontend.containerPort }} - name: http - protocol: TCP - startupProbe: - httpGet: - path: / - port: http - failureThreshold: 30 - periodSeconds: 10 - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {{- toYaml .Values.frontend.resources | nindent 12 }} - restartPolicy: Always diff --git a/.framework/rails/charts/templates/anythink-frontend-service.yaml b/.framework/rails/charts/templates/anythink-frontend-service.yaml deleted file mode 100644 index 217f8c5..0000000 --- a/.framework/rails/charts/templates/anythink-frontend-service.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: {{ .Values.frontend.serviceName }} - name: {{ .Values.frontend.serviceName }} -spec: - ports: - - name: "{{ .Values.frontend.containerPort }}" - port: {{ .Values.frontend.containerPort }} - targetPort: {{ .Values.frontend.containerPort }} - selector: - app: {{ .Values.frontend.serviceName }} diff --git a/.framework/rails/charts/templates/database-deployment.yaml b/.framework/rails/charts/templates/database-deployment.yaml deleted file mode 100644 index 667a23a..0000000 --- a/.framework/rails/charts/templates/database-deployment.yaml +++ /dev/null @@ -1,42 +0,0 @@ -{{- if .Values.database.deploy }} -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app: {{ .Values.database.serviceName }} - name: {{ .Values.database.serviceName }} -spec: - replicas: 1 - selector: - matchLabels: - app: {{ .Values.database.serviceName }} - strategy: - type: Recreate - template: - metadata: - labels: - app: {{ .Values.database.serviceName }} - spec: - containers: - - image: "{{ .Values.database.image.repository }}:{{ .Values.database.image.tag }}" - name: {{ .Values.database.serviceName }} - env: - - name: POSTGRES_HOST_AUTH_METHOD - value: trust - - name: POSTGRES_USER - value: {{ .Values.database.env.userName }} - - name: POSTGRES_PASSWORD - value: {{ .Values.database.env.password }} - imagePullPolicy: {{ .Values.database.image.pullPolicy }} - ports: - - containerPort: {{ .Values.database.containerPort }} - resources: {} - volumeMounts: - - mountPath: /data/db - name: {{ .Values.database.serviceName }}-0 - restartPolicy: Always - volumes: - - name: {{ .Values.database.serviceName }}-0 - persistentVolumeClaim: - claimName: {{ .Values.database.serviceName }}-0 -{{- end }} diff --git a/.framework/rails/charts/templates/database-pvc.yaml b/.framework/rails/charts/templates/database-pvc.yaml deleted file mode 100644 index 88517f3..0000000 --- a/.framework/rails/charts/templates/database-pvc.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - labels: - app: {{ .Values.database.serviceName }}-0 - name: {{ .Values.database.serviceName }}-0 -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Mi diff --git a/.framework/rails/charts/templates/database-service.yaml b/.framework/rails/charts/templates/database-service.yaml deleted file mode 100644 index 80b47d3..0000000 --- a/.framework/rails/charts/templates/database-service.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - app: {{ .Values.database.serviceName }} - name: {{ .Values.database.serviceName }} -spec: - ports: - - name: "{{ .Values.database.servicePort }}" - port: {{ .Values.database.servicePort }} - targetPort: {{ .Values.database.servicePort }} - selector: - app: {{ .Values.database.serviceName }} diff --git a/.framework/rails/charts/values.yaml b/.framework/rails/charts/values.yaml deleted file mode 100644 index 026452c..0000000 --- a/.framework/rails/charts/values.yaml +++ /dev/null @@ -1,70 +0,0 @@ -clusterEnv: "" -productionBackendHost: "prod.anythink.market" -stagingBackendHost: "staging.anythink.market" - -backend: - serviceName: anythink-backend - containerPort: 3000 - replicaCount: 1 - service: - type: ClusterIP - port: 80 - image: - repository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/anythink-backend - stagingRepository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/staging-anythink-backend - pullPolicy: Always - # Overrides the image tag whose default is the chart appVersion. - tag: "latest" - resources: - limits: - cpu: 100m - memory: 512Mi - requests: - cpu: 100m - memory: 128Mi - -frontend: - serviceName: anythink-frontend - containerPort: 3001 - replicaCount: 1 - service: - type: ClusterIP - port: 80 - image: - repository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/anythink-frontend - stagingRepository: 498915426792.dkr.ecr.us-east-2.amazonaws.com/staging-anythink-frontend - pullPolicy: Always - # Overrides the image tag whose default is the chart appVersion. - tag: "latest" - resources: - limits: - cpu: 600m - memory: 768Mi - requests: - cpu: 100m - memory: 128Mi - -database: - deploy: true - connectionProtocol: postgres:// - serviceName: postgres-rails - containerPort: 5433 - servicePort: 5432 - replicaCount: 1 - env: - userName: user - password: password - service: - type: ClusterIP - port: 80 - image: - repository: postgres - pullPolicy: IfNotPresent - tag: "latest" - resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi diff --git a/.framework/rails/docker-compose.yml b/.framework/rails/docker-compose.yml deleted file mode 100644 index 9712c19..0000000 --- a/.framework/rails/docker-compose.yml +++ /dev/null @@ -1,60 +0,0 @@ -services: - anythink-backend-rails: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-backend-rails:latest - container_name: anythink-backend-rails - command: sh -c "cd backend && bundle install && /wait-for-it.sh postgres-rails:5432 -q -t 60 && ./start_rails.sh" - - working_dir: /usr/src - volumes: - - ./:/usr/src/ - ports: - - "3000:3000" - environment: - - RAILS_ENV=development - - PORT=3000 - - DATABASE_URL=postgres://user:@postgres-rails:5432 - - GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN=${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN} - depends_on: - - "postgres-rails" - - anythink-frontend-react: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-frontend-react:latest - container_name: anythink-frontend-react - command: sh -c "cd frontend && /wait-for-it.sh anythink-backend-rails:3000 -t 120 --strict -- curl --head -X GET --retry 30 --retry-connrefused --retry-delay 1 anythink-backend-rails:3000/api/ping && yarn start" - environment: - - NODE_ENV=development - - PORT=3001 - - REACT_APP_BACKEND_URL=${CODESPACE_BACKEND_URL:-http://localhost:3000} - - WDS_SOCKET_PORT=${CODESPACE_WDS_SOCKET_PORT:-3001} - working_dir: /usr/src - volumes: - - ./:/usr/src/ - - /usr/src/frontend/node_modules - ports: - - "3001:3001" - depends_on: - - "anythink-backend-rails" - - postgres-rails: - container_name: postgres-rails - restart: on-failure - image: postgres - logging: - driver: none - environment: - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_USER: user - POSTGRES_PASSWORD: password - volumes: - - ~/posgres/data:/data/db - ports: - - '5433:5432' - - anythink-ack: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-ack:latest - container_name: anythink-ack - environment: - - GITHUB_TOKEN=$GITHUB_TOKEN - - CODESPACE_NAME=$CODESPACE_NAME - depends_on: - - "anythink-frontend-react" diff --git a/.github/.workflows/k8s.yml b/.github/workflows/k8s.yml similarity index 100% rename from .github/.workflows/k8s.yml rename to .github/workflows/k8s.yml diff --git a/.github/workflows/test_e2e_java.yml b/.github/workflows/test_e2e_java.yml deleted file mode 100644 index c68548d..0000000 --- a/.github/workflows/test_e2e_java.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: E2E Java - -on: - pull_request: - -jobs: - ci-checks: - name: Pr checks - runs-on: ubuntu-20.04 - timeout-minutes: 6 - - services: - postgres: - image: postgres:13 - env: - POSTGRES_PASSWORD: postgres - SECRET_KEY: secret - POSTGRES_DB: anythink-market - ports: - - 5432:5432 - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - steps: - - name: Check out project - uses: actions/checkout@v2 - - - uses: actions/setup-java@v4 - with: - working-directory: .framework/java/backend - java-version: 21 - distribution: adopt - - - uses: oNaiPs/secrets-to-env-action@v1 - with: - secrets: ${{ toJSON(secrets) }} - - - run: ./gradlew build - working-directory: .framework/java/backend - - - run: ENGINE_BASE_URL=http://localhost:3003 WILCO_ID=0 SPRING_DATASOURCE_URL=jdbc:postgresql://localhost/anythink-market SPRING_DATASOURCE_USERNAME=postgres SPRING_DATASOURCE_PASSWORD=postgres ./gradlew bootRun >& /dev/null & - working-directory: .framework/java/backend - - - name: Run checks - uses: actions/setup-node@v3 - with: - node-version: "16" - cache: "yarn" - cache-dependency-path: tests/e2e/yarn.lock - - - run: yarn install - working-directory: tests/e2e/ - - - run: yarn wait-on http://localhost:3000/health --timeout 20000 - working-directory: tests/e2e/ - - - run: yarn jest -c jest.sequential.config.js --maxWorkers=1 # TODO skip concurrent tests until fixed - working-directory: tests/e2e/ diff --git a/.github/workflows/test_e2e_lint.yml b/.github/workflows/test_e2e_lint.yml deleted file mode 100644 index 4d8e272..0000000 --- a/.github/workflows/test_e2e_lint.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Lint - -on: - pull_request: - -jobs: - lint: - name: Pr checks - runs-on: ubuntu-20.04 - timeout-minutes: 6 - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} - - - name: Run checks - uses: actions/setup-node@v3 - with: - node-version: "16" - cache: "yarn" - cache-dependency-path: tests/e2e/ - - - run: yarn install - working-directory: tests/e2e - - - run: yarn lint - working-directory: tests/e2e diff --git a/.github/workflows/test_e2e_node.yml b/.github/workflows/test_e2e_node.yml deleted file mode 100644 index a0f363d..0000000 --- a/.github/workflows/test_e2e_node.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: E2E Node - -on: - pull_request: - -jobs: - ci-checks: - name: Pr checks - runs-on: ubuntu-20.04 - timeout-minutes: 6 - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} - - - name: Run checks - uses: actions/setup-node@v3 - with: - node-version: "16" - cache: "yarn" - cache-dependency-path: .framework/node/backend - - - uses: oNaiPs/secrets-to-env-action@v1 - with: - secrets: ${{ toJSON(secrets) }} - - - name: Start MongoDB - uses: supercharge/mongodb-github-action@1.6.0 - with: - mongodb-version: "4.4" - - - run: yarn install - working-directory: .framework/node/backend - - - run: ENGINE_BASE_URL=http://localhost:3003 WILCO_ID=0 MONGODB_URI=mongodb://localhost/anythink-market yarn start >& /dev/null & - working-directory: .framework/node/backend - - - run: yarn install - working-directory: tests/e2e/ - - - run: yarn wait-on http://localhost:3000/health --timeout 5000 - working-directory: tests/e2e/ - - - run: yarn test - working-directory: tests/e2e/ diff --git a/.github/workflows/test_e2e_python.yml b/.github/workflows/test_e2e_python.yml deleted file mode 100644 index e2684b6..0000000 --- a/.github/workflows/test_e2e_python.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: E2E Python - -on: - pull_request: - -jobs: - ci-checks: - name: Pr checks - runs-on: ubuntu-20.04 - timeout-minutes: 6 - - services: - postgres: - image: postgres:13 - env: - POSTGRES_PASSWORD: postgres - SECRET_KEY: secret - POSTGRES_DB: anythink-market - ports: - - 5432:5432 - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - steps: - - name: Check out project - uses: actions/checkout@v2 - - - name: Use Python - uses: actions/setup-python@v4 - with: - python-version: "3.9.13" - cache: 'pip' - - - uses: oNaiPs/secrets-to-env-action@v1 - with: - secrets: ${{ toJSON(secrets) }} - - - run: pip install -r requirements.txt - working-directory: .framework/python/backend - - - run: pip install gunicorn - working-directory: .framework/python/backend - - - name: Run postgres migration - run: SECRET_KEY=secret DATABASE_URL=postgresql://postgres:postgres@localhost/anythink-market alembic upgrade head - working-directory: .framework/python/backend - - - name: Run python server - run: ENGINE_BASE_URL=http://localhost:3003 WILCO_ID=0 SECRET_KEY=secret DATABASE_URL=postgresql://postgres:postgres@localhost/anythink-market gunicorn app.main:app --worker-class=uvicorn.workers.UvicornWorker --bind=0.0.0.0:3000 --workers=5 >& /dev/null & - working-directory: .framework/python/backend - - - name: Run checks - uses: actions/setup-node@v3 - with: - node-version: "16" - cache: "yarn" - cache-dependency-path: tests/e2e/yarn.lock - - - run: yarn install - working-directory: tests/e2e/ - - - run: yarn wait-on -v http-get://0.0.0.0:3000/health --timeout 5000 - working-directory: tests/e2e/ - - - run: yarn test - working-directory: tests/e2e/ diff --git a/.github/workflows/test_e2e_rails.yml b/.github/workflows/test_e2e_rails.yml deleted file mode 100644 index 2c2c6ec..0000000 --- a/.github/workflows/test_e2e_rails.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: E2E Rails - -on: - pull_request: - -jobs: - ci-checks: - name: Pr checks - runs-on: ubuntu-20.04 - timeout-minutes: 6 - - services: - postgres: - image: postgres:13 - env: - POSTGRES_PASSWORD: postgres - ports: - - 5432:5432 - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - steps: - - name: Check out project - uses: actions/checkout@v2 - - - uses: ruby/setup-ruby@v1 - with: - working-directory: .framework/rails/backend - bundler-cache: true - - - uses: oNaiPs/secrets-to-env-action@v1 - with: - secrets: ${{ toJSON(secrets) }} - - - run: bundle install - working-directory: .framework/rails/backend - - - run: DATABASE_URL=postgresql://postgres:postgres@localhost/anythink-market bin/rails db:create db:migrate - working-directory: .framework/rails/backend - - - run: ENGINE_BASE_URL=http://localhost:3003 WILCO_ID=0 DATABASE_URL=postgresql://postgres:postgres@localhost/anythink-market bin/rails s >& /dev/null & - working-directory: .framework/rails/backend - - - name: Run checks - uses: actions/setup-node@v3 - with: - node-version: "16" - cache: "yarn" - cache-dependency-path: tests/e2e/yarn.lock - - - run: yarn install - working-directory: tests/e2e/ - - - run: yarn wait-on http://localhost:3000/health --timeout 5000 - working-directory: tests/e2e/ - - - run: yarn test - working-directory: tests/e2e/ diff --git a/.github/workflows/test_frontend_react.yml b/.github/workflows/test_frontend_react.yml deleted file mode 100644 index 32b89cb..0000000 --- a/.github/workflows/test_frontend_react.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Playwright Tests -on: - pull_request: - -jobs: - test: - timeout-minutes: 6 - runs-on: ubuntu-20.04 - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: ${{ github.head_ref }} - - name: Run checks - uses: actions/setup-node@v3 - with: - node-version: "16" - cache: "yarn" - cache-dependency-path: .framework/react/frontend - - - name: Install dependencies - run: yarn install - working-directory: .framework/react/frontend - - - name: Run frontend client - run: WILCO_ID=0 REACT_APP_BACKEND_URL=http://localhost:3001 yarn start >& /dev/null & - working-directory: .framework/react/frontend - - - name: Install test deps - run: yarn install - working-directory: tests/frontend - - - name: Run Playwright tests - run: yarn test - working-directory: tests/frontend diff --git a/.github/workflows/wilco-actions.yml b/.github/workflows/wilco-actions.yml new file mode 100644 index 0000000..15fa886 --- /dev/null +++ b/.github/workflows/wilco-actions.yml @@ -0,0 +1,34 @@ +on: + pull_request: + branches: + - main + +jobs: + wilco: + runs-on: ubuntu-20.04 + timeout-minutes: 10 + name: Pr checks + + steps: + - name: Check out project + uses: actions/checkout@v2 + + - name: Use Node.js + uses: actions/setup-node@v3 + with: + node-version: "16" + + - name: Start MongoDB + uses: supercharge/mongodb-github-action@1.6.0 + with: + mongodb-version: "4.4" + + - uses: oNaiPs/secrets-to-env-action@v1 + with: + secrets: ${{ toJSON(secrets) }} + + - name: Wilco checks + id: Wilco + uses: trywilco/actions@main + with: + engine: ${{ secrets.WILCO_ENGINE_URL }} diff --git a/.wilco b/.wilco new file mode 100644 index 0000000..6442b72 --- /dev/null +++ b/.wilco @@ -0,0 +1 @@ +6722761be9baffc598b32a73 \ No newline at end of file diff --git a/.framework/node/backend/.gitignore b/backend/.gitignore similarity index 100% rename from .framework/node/backend/.gitignore rename to backend/.gitignore diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..e779cce --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1 @@ +FROM public.ecr.aws/v0a2l7y2/wilco/anythink-backend-node:latest diff --git a/.framework/node/backend/Dockerfile.aws b/backend/Dockerfile.aws similarity index 100% rename from .framework/node/backend/Dockerfile.aws rename to backend/Dockerfile.aws diff --git a/.framework/node/backend/README.md b/backend/README.md similarity index 100% rename from .framework/node/backend/README.md rename to backend/README.md diff --git a/.framework/node/backend/app.js b/backend/app.js similarity index 98% rename from .framework/node/backend/app.js rename to backend/app.js index 1a1d5a8..f4e7f35 100644 --- a/.framework/node/backend/app.js +++ b/backend/app.js @@ -27,7 +27,7 @@ app.use(express.static(__dirname + "/public")); app.use( session({ - secret: "e6F9KvSDf4dyXj", + secret: "secret", cookie: { maxAge: 60000 }, resave: false, saveUninitialized: false diff --git a/.framework/node/backend/config/index.js b/backend/config/index.js similarity index 78% rename from .framework/node/backend/config/index.js rename to backend/config/index.js index f69b995..1bf9d6a 100644 --- a/.framework/node/backend/config/index.js +++ b/backend/config/index.js @@ -1,3 +1,3 @@ module.exports = { - secret: process.env.NODE_ENV === 'production' ? process.env.SECRET : 'e6F9KvSDf4dyXj' + secret: process.env.NODE_ENV === 'production' ? process.env.SECRET : 'secret' }; diff --git a/.framework/node/backend/config/passport.js b/backend/config/passport.js similarity index 100% rename from .framework/node/backend/config/passport.js rename to backend/config/passport.js diff --git a/.framework/node/backend/lib/event.js b/backend/lib/event.js similarity index 100% rename from .framework/node/backend/lib/event.js rename to backend/lib/event.js diff --git a/.framework/node/backend/models/Comment.js b/backend/models/Comment.js similarity index 100% rename from .framework/node/backend/models/Comment.js rename to backend/models/Comment.js diff --git a/.framework/node/backend/models/Item.js b/backend/models/Item.js similarity index 100% rename from .framework/node/backend/models/Item.js rename to backend/models/Item.js diff --git a/.framework/node/backend/models/User.js b/backend/models/User.js similarity index 100% rename from .framework/node/backend/models/User.js rename to backend/models/User.js diff --git a/.framework/node/backend/package.json b/backend/package.json similarity index 100% rename from .framework/node/backend/package.json rename to backend/package.json diff --git a/.framework/node/backend/public/.keep b/backend/public/.keep similarity index 100% rename from .framework/node/backend/public/.keep rename to backend/public/.keep diff --git a/backend/routes/api/comments.js b/backend/routes/api/comments.js new file mode 100644 index 0000000..47b0bc5 --- /dev/null +++ b/backend/routes/api/comments.js @@ -0,0 +1,5 @@ +const router = require("express").Router(); +const mongoose = require("mongoose"); +const Comment = mongoose.model("Comment"); + +module.exports = router; diff --git a/.framework/node/backend/routes/api/index.js b/backend/routes/api/index.js similarity index 92% rename from .framework/node/backend/routes/api/index.js rename to backend/routes/api/index.js index 380d027..9f23863 100644 --- a/.framework/node/backend/routes/api/index.js +++ b/backend/routes/api/index.js @@ -4,6 +4,7 @@ router.use('/', require('./users')); router.use('/profiles', require('./profiles')); router.use('/items', require('./items')); router.use('/tags', require('./tags')); +router.use('/comments', require('./comments')); router.use('/ping', require('./ping')); router.use(function(err, req, res, next){ diff --git a/.framework/node/backend/routes/api/items.js b/backend/routes/api/items.js similarity index 100% rename from .framework/node/backend/routes/api/items.js rename to backend/routes/api/items.js diff --git a/.framework/node/backend/routes/api/ping.js b/backend/routes/api/ping.js similarity index 100% rename from .framework/node/backend/routes/api/ping.js rename to backend/routes/api/ping.js diff --git a/.framework/node/backend/routes/api/profiles.js b/backend/routes/api/profiles.js similarity index 100% rename from .framework/node/backend/routes/api/profiles.js rename to backend/routes/api/profiles.js diff --git a/.framework/node/backend/routes/api/tags.js b/backend/routes/api/tags.js similarity index 100% rename from .framework/node/backend/routes/api/tags.js rename to backend/routes/api/tags.js diff --git a/.framework/node/backend/routes/api/users.js b/backend/routes/api/users.js similarity index 100% rename from .framework/node/backend/routes/api/users.js rename to backend/routes/api/users.js diff --git a/.framework/node/backend/routes/auth.js b/backend/routes/auth.js similarity index 100% rename from .framework/node/backend/routes/auth.js rename to backend/routes/auth.js diff --git a/.framework/node/backend/routes/index.js b/backend/routes/index.js similarity index 100% rename from .framework/node/backend/routes/index.js rename to backend/routes/index.js diff --git a/.framework/node/backend/scripts/seeds.js b/backend/scripts/seeds.js similarity index 100% rename from .framework/node/backend/scripts/seeds.js rename to backend/scripts/seeds.js diff --git a/.framework/node/backend/seeds.sh b/backend/seeds.sh similarity index 100% rename from .framework/node/backend/seeds.sh rename to backend/seeds.sh diff --git a/.framework/node/backend/start.sh b/backend/start.sh similarity index 100% rename from .framework/node/backend/start.sh rename to backend/start.sh diff --git a/.framework/node/backend/tests/api-tests.postman.json b/backend/tests/api-tests.postman.json similarity index 100% rename from .framework/node/backend/tests/api-tests.postman.json rename to backend/tests/api-tests.postman.json diff --git a/.framework/node/backend/tests/env-api-tests.postman.json b/backend/tests/env-api-tests.postman.json similarity index 100% rename from .framework/node/backend/tests/env-api-tests.postman.json rename to backend/tests/env-api-tests.postman.json diff --git a/.framework/node/backend/yarn.lock b/backend/yarn.lock similarity index 100% rename from .framework/node/backend/yarn.lock rename to backend/yarn.lock diff --git a/.framework/java/charts/.helmignore b/charts/.helmignore similarity index 100% rename from .framework/java/charts/.helmignore rename to charts/.helmignore diff --git a/.framework/java/charts/Chart.yaml b/charts/Chart.yaml similarity index 100% rename from .framework/java/charts/Chart.yaml rename to charts/Chart.yaml diff --git a/.framework/java/charts/templates/_helpers.yml b/charts/templates/_helpers.yml similarity index 100% rename from .framework/java/charts/templates/_helpers.yml rename to charts/templates/_helpers.yml diff --git a/.framework/node/charts/templates/anythink-backend-deployment.yaml b/charts/templates/anythink-backend-deployment.yaml similarity index 100% rename from .framework/node/charts/templates/anythink-backend-deployment.yaml rename to charts/templates/anythink-backend-deployment.yaml diff --git a/.framework/java/charts/templates/anythink-backend-service.yaml b/charts/templates/anythink-backend-service.yaml similarity index 100% rename from .framework/java/charts/templates/anythink-backend-service.yaml rename to charts/templates/anythink-backend-service.yaml diff --git a/.framework/java/charts/templates/anythink-frontend-deployment.yaml b/charts/templates/anythink-frontend-deployment.yaml similarity index 100% rename from .framework/java/charts/templates/anythink-frontend-deployment.yaml rename to charts/templates/anythink-frontend-deployment.yaml diff --git a/.framework/java/charts/templates/anythink-frontend-service.yaml b/charts/templates/anythink-frontend-service.yaml similarity index 100% rename from .framework/java/charts/templates/anythink-frontend-service.yaml rename to charts/templates/anythink-frontend-service.yaml diff --git a/.framework/node/charts/templates/database-deployment.yaml b/charts/templates/database-deployment.yaml similarity index 100% rename from .framework/node/charts/templates/database-deployment.yaml rename to charts/templates/database-deployment.yaml diff --git a/.framework/java/charts/templates/database-pvc.yaml b/charts/templates/database-pvc.yaml similarity index 100% rename from .framework/java/charts/templates/database-pvc.yaml rename to charts/templates/database-pvc.yaml diff --git a/.framework/java/charts/templates/database-service.yaml b/charts/templates/database-service.yaml similarity index 100% rename from .framework/java/charts/templates/database-service.yaml rename to charts/templates/database-service.yaml diff --git a/.framework/node/charts/values.yaml b/charts/values.yaml similarity index 100% rename from .framework/node/charts/values.yaml rename to charts/values.yaml diff --git a/.framework/node/docker-compose.yml b/docker-compose.yml similarity index 75% rename from .framework/node/docker-compose.yml rename to docker-compose.yml index da550a3..c233da8 100644 --- a/.framework/node/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,9 @@ +version: "3.8" services: anythink-backend-node: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-backend-node:latest + build: ./backend container_name: anythink-backend-node - command: sh -c "cd backend && /wait-for-it.sh mongodb-node:27017 -q -t 60 && yarn dev" + command: sh -c "cd backend && yarn install && /wait-for-it.sh mongodb-node:27017 -q -t 60 && yarn dev" environment: - NODE_ENV=development @@ -19,9 +20,9 @@ services: - "mongodb-node" anythink-frontend-react: - image: public.ecr.aws/v0a2l7y2/wilco/anythink-frontend-react:latest + build: ./frontend container_name: anythink-frontend-react - command: sh -c "cd frontend && /wait-for-it.sh anythink-backend-node:3000 -t 120 --strict -- curl --head -X GET --retry 30 --retry-connrefused --retry-delay 1 anythink-backend-node:3000/api/ping && yarn start" + command: sh -c "cd frontend && yarn install && /wait-for-it.sh anythink-backend-node:3000 -t 120 --strict -- curl --head -X GET --retry 30 --retry-connrefused --retry-delay 1 anythink-backend-node:3000/api/ping && yarn start" environment: - NODE_ENV=development - PORT=3001 diff --git a/.framework/react/frontend/.eslintignore b/frontend/.eslintignore similarity index 100% rename from .framework/react/frontend/.eslintignore rename to frontend/.eslintignore diff --git a/.framework/react/frontend/.gitignore b/frontend/.gitignore similarity index 100% rename from .framework/react/frontend/.gitignore rename to frontend/.gitignore diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..c8ba554 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1 @@ +FROM public.ecr.aws/v0a2l7y2/wilco/anythink-frontend-react:latest diff --git a/.framework/react/frontend/Dockerfile.aws b/frontend/Dockerfile.aws similarity index 100% rename from .framework/react/frontend/Dockerfile.aws rename to frontend/Dockerfile.aws diff --git a/.framework/react/frontend/jest.config.js b/frontend/jest.config.js similarity index 100% rename from .framework/react/frontend/jest.config.js rename to frontend/jest.config.js diff --git a/.framework/react/frontend/package.json b/frontend/package.json similarity index 100% rename from .framework/react/frontend/package.json rename to frontend/package.json diff --git a/.framework/react/frontend/public/50precentoff.png b/frontend/public/50precentoff.png similarity index 100% rename from .framework/react/frontend/public/50precentoff.png rename to frontend/public/50precentoff.png diff --git a/.framework/react/frontend/public/favicon.ico b/frontend/public/favicon.ico similarity index 100% rename from .framework/react/frontend/public/favicon.ico rename to frontend/public/favicon.ico diff --git a/.framework/react/frontend/public/index.html b/frontend/public/index.html similarity index 100% rename from .framework/react/frontend/public/index.html rename to frontend/public/index.html diff --git a/.framework/react/frontend/public/placeholder.png b/frontend/public/placeholder.png similarity index 100% rename from .framework/react/frontend/public/placeholder.png rename to frontend/public/placeholder.png diff --git a/.framework/react/frontend/public/style.css b/frontend/public/style.css similarity index 100% rename from .framework/react/frontend/public/style.css rename to frontend/public/style.css diff --git a/.framework/react/frontend/public/sunray.jpeg b/frontend/public/sunray.jpeg similarity index 100% rename from .framework/react/frontend/public/sunray.jpeg rename to frontend/public/sunray.jpeg diff --git a/.framework/react/frontend/public/verified_seller.svg b/frontend/public/verified_seller.svg similarity index 100% rename from .framework/react/frontend/public/verified_seller.svg rename to frontend/public/verified_seller.svg diff --git a/.framework/react/frontend/readme.md b/frontend/readme.md similarity index 100% rename from .framework/react/frontend/readme.md rename to frontend/readme.md diff --git a/.framework/react/frontend/src/agent.js b/frontend/src/agent.js similarity index 100% rename from .framework/react/frontend/src/agent.js rename to frontend/src/agent.js diff --git a/.framework/react/frontend/src/components/App.js b/frontend/src/components/App.js similarity index 100% rename from .framework/react/frontend/src/components/App.js rename to frontend/src/components/App.js diff --git a/.framework/react/frontend/src/components/Editor.js b/frontend/src/components/Editor.js similarity index 100% rename from .framework/react/frontend/src/components/Editor.js rename to frontend/src/components/Editor.js diff --git a/.framework/react/frontend/src/components/Header.js b/frontend/src/components/Header.js similarity index 100% rename from .framework/react/frontend/src/components/Header.js rename to frontend/src/components/Header.js diff --git a/.framework/react/frontend/src/components/Home/Banner.js b/frontend/src/components/Home/Banner.js similarity index 100% rename from .framework/react/frontend/src/components/Home/Banner.js rename to frontend/src/components/Home/Banner.js diff --git a/.framework/react/frontend/src/components/Home/MainView.js b/frontend/src/components/Home/MainView.js similarity index 100% rename from .framework/react/frontend/src/components/Home/MainView.js rename to frontend/src/components/Home/MainView.js diff --git a/.framework/react/frontend/src/components/Home/Tags.js b/frontend/src/components/Home/Tags.js similarity index 100% rename from .framework/react/frontend/src/components/Home/Tags.js rename to frontend/src/components/Home/Tags.js diff --git a/.framework/react/frontend/src/components/Home/index.js b/frontend/src/components/Home/index.js similarity index 100% rename from .framework/react/frontend/src/components/Home/index.js rename to frontend/src/components/Home/index.js diff --git a/.framework/react/frontend/src/components/Item/Comment.js b/frontend/src/components/Item/Comment.js similarity index 100% rename from .framework/react/frontend/src/components/Item/Comment.js rename to frontend/src/components/Item/Comment.js diff --git a/.framework/react/frontend/src/components/Item/CommentContainer.js b/frontend/src/components/Item/CommentContainer.js similarity index 100% rename from .framework/react/frontend/src/components/Item/CommentContainer.js rename to frontend/src/components/Item/CommentContainer.js diff --git a/.framework/react/frontend/src/components/Item/CommentInput.js b/frontend/src/components/Item/CommentInput.js similarity index 100% rename from .framework/react/frontend/src/components/Item/CommentInput.js rename to frontend/src/components/Item/CommentInput.js diff --git a/.framework/react/frontend/src/components/Item/CommentList.js b/frontend/src/components/Item/CommentList.js similarity index 100% rename from .framework/react/frontend/src/components/Item/CommentList.js rename to frontend/src/components/Item/CommentList.js diff --git a/.framework/react/frontend/src/components/Item/DeleteButton.js b/frontend/src/components/Item/DeleteButton.js similarity index 100% rename from .framework/react/frontend/src/components/Item/DeleteButton.js rename to frontend/src/components/Item/DeleteButton.js diff --git a/.framework/react/frontend/src/components/Item/ItemActions.js b/frontend/src/components/Item/ItemActions.js similarity index 100% rename from .framework/react/frontend/src/components/Item/ItemActions.js rename to frontend/src/components/Item/ItemActions.js diff --git a/.framework/react/frontend/src/components/Item/ItemMeta.js b/frontend/src/components/Item/ItemMeta.js similarity index 100% rename from .framework/react/frontend/src/components/Item/ItemMeta.js rename to frontend/src/components/Item/ItemMeta.js diff --git a/.framework/react/frontend/src/components/Item/index.js b/frontend/src/components/Item/index.js similarity index 100% rename from .framework/react/frontend/src/components/Item/index.js rename to frontend/src/components/Item/index.js diff --git a/.framework/react/frontend/src/components/Item/utils/ItemFetcher.js b/frontend/src/components/Item/utils/ItemFetcher.js similarity index 100% rename from .framework/react/frontend/src/components/Item/utils/ItemFetcher.js rename to frontend/src/components/Item/utils/ItemFetcher.js diff --git a/.framework/react/frontend/src/components/ItemList.js b/frontend/src/components/ItemList.js similarity index 100% rename from .framework/react/frontend/src/components/ItemList.js rename to frontend/src/components/ItemList.js diff --git a/.framework/react/frontend/src/components/ItemPreview.js b/frontend/src/components/ItemPreview.js similarity index 100% rename from .framework/react/frontend/src/components/ItemPreview.js rename to frontend/src/components/ItemPreview.js diff --git a/.framework/react/frontend/src/components/ListErrors.js b/frontend/src/components/ListErrors.js similarity index 100% rename from .framework/react/frontend/src/components/ListErrors.js rename to frontend/src/components/ListErrors.js diff --git a/.framework/react/frontend/src/components/ListPagination.js b/frontend/src/components/ListPagination.js similarity index 100% rename from .framework/react/frontend/src/components/ListPagination.js rename to frontend/src/components/ListPagination.js diff --git a/.framework/react/frontend/src/components/Login.js b/frontend/src/components/Login.js similarity index 100% rename from .framework/react/frontend/src/components/Login.js rename to frontend/src/components/Login.js diff --git a/.framework/react/frontend/src/components/Profile.js b/frontend/src/components/Profile.js similarity index 100% rename from .framework/react/frontend/src/components/Profile.js rename to frontend/src/components/Profile.js diff --git a/.framework/react/frontend/src/components/ProfileFavorites.js b/frontend/src/components/ProfileFavorites.js similarity index 100% rename from .framework/react/frontend/src/components/ProfileFavorites.js rename to frontend/src/components/ProfileFavorites.js diff --git a/.framework/react/frontend/src/components/Register.js b/frontend/src/components/Register.js similarity index 100% rename from .framework/react/frontend/src/components/Register.js rename to frontend/src/components/Register.js diff --git a/.framework/react/frontend/src/components/Settings.js b/frontend/src/components/Settings.js similarity index 100% rename from .framework/react/frontend/src/components/Settings.js rename to frontend/src/components/Settings.js diff --git a/.framework/react/frontend/src/components/commons.js b/frontend/src/components/commons.js similarity index 100% rename from .framework/react/frontend/src/components/commons.js rename to frontend/src/components/commons.js diff --git a/.framework/react/frontend/src/constants/actionTypes.js b/frontend/src/constants/actionTypes.js similarity index 100% rename from .framework/react/frontend/src/constants/actionTypes.js rename to frontend/src/constants/actionTypes.js diff --git a/.framework/react/frontend/src/custom.scss b/frontend/src/custom.scss similarity index 100% rename from .framework/react/frontend/src/custom.scss rename to frontend/src/custom.scss diff --git a/.framework/react/frontend/src/imgs/background.png b/frontend/src/imgs/background.png similarity index 100% rename from .framework/react/frontend/src/imgs/background.png rename to frontend/src/imgs/background.png diff --git a/.framework/react/frontend/src/imgs/logo.png b/frontend/src/imgs/logo.png similarity index 100% rename from .framework/react/frontend/src/imgs/logo.png rename to frontend/src/imgs/logo.png diff --git a/.framework/react/frontend/src/imgs/topbar_logo.png b/frontend/src/imgs/topbar_logo.png similarity index 100% rename from .framework/react/frontend/src/imgs/topbar_logo.png rename to frontend/src/imgs/topbar_logo.png diff --git a/.framework/react/frontend/src/index.js b/frontend/src/index.js similarity index 100% rename from .framework/react/frontend/src/index.js rename to frontend/src/index.js diff --git a/.framework/react/frontend/src/middleware.js b/frontend/src/middleware.js similarity index 100% rename from .framework/react/frontend/src/middleware.js rename to frontend/src/middleware.js diff --git a/.framework/react/frontend/src/reducer.js b/frontend/src/reducer.js similarity index 100% rename from .framework/react/frontend/src/reducer.js rename to frontend/src/reducer.js diff --git a/.framework/react/frontend/src/reducers/auth.js b/frontend/src/reducers/auth.js similarity index 100% rename from .framework/react/frontend/src/reducers/auth.js rename to frontend/src/reducers/auth.js diff --git a/.framework/react/frontend/src/reducers/common.js b/frontend/src/reducers/common.js similarity index 100% rename from .framework/react/frontend/src/reducers/common.js rename to frontend/src/reducers/common.js diff --git a/.framework/react/frontend/src/reducers/editor.js b/frontend/src/reducers/editor.js similarity index 100% rename from .framework/react/frontend/src/reducers/editor.js rename to frontend/src/reducers/editor.js diff --git a/.framework/react/frontend/src/reducers/home.js b/frontend/src/reducers/home.js similarity index 100% rename from .framework/react/frontend/src/reducers/home.js rename to frontend/src/reducers/home.js diff --git a/.framework/react/frontend/src/reducers/item.js b/frontend/src/reducers/item.js similarity index 100% rename from .framework/react/frontend/src/reducers/item.js rename to frontend/src/reducers/item.js diff --git a/.framework/react/frontend/src/reducers/itemList.js b/frontend/src/reducers/itemList.js similarity index 100% rename from .framework/react/frontend/src/reducers/itemList.js rename to frontend/src/reducers/itemList.js diff --git a/.framework/react/frontend/src/reducers/profile.js b/frontend/src/reducers/profile.js similarity index 100% rename from .framework/react/frontend/src/reducers/profile.js rename to frontend/src/reducers/profile.js diff --git a/.framework/react/frontend/src/reducers/settings.js b/frontend/src/reducers/settings.js similarity index 100% rename from .framework/react/frontend/src/reducers/settings.js rename to frontend/src/reducers/settings.js diff --git a/.framework/react/frontend/src/setupTests.js b/frontend/src/setupTests.js similarity index 100% rename from .framework/react/frontend/src/setupTests.js rename to frontend/src/setupTests.js diff --git a/.framework/react/frontend/src/store.js b/frontend/src/store.js similarity index 100% rename from .framework/react/frontend/src/store.js rename to frontend/src/store.js diff --git a/.framework/react/frontend/src/tests/components/Header.test.js b/frontend/src/tests/components/Header.test.js similarity index 100% rename from .framework/react/frontend/src/tests/components/Header.test.js rename to frontend/src/tests/components/Header.test.js diff --git a/.framework/react/frontend/src/tests/components/__snapshots__/Header.test.js.snap b/frontend/src/tests/components/__snapshots__/Header.test.js.snap similarity index 100% rename from .framework/react/frontend/src/tests/components/__snapshots__/Header.test.js.snap rename to frontend/src/tests/components/__snapshots__/Header.test.js.snap diff --git a/.framework/react/frontend/src/tests/item/CommentInput.test.js b/frontend/src/tests/item/CommentInput.test.js similarity index 100% rename from .framework/react/frontend/src/tests/item/CommentInput.test.js rename to frontend/src/tests/item/CommentInput.test.js diff --git a/.framework/react/frontend/src/tests/item/__snapshots__/CommentInput.test.js.snap b/frontend/src/tests/item/__snapshots__/CommentInput.test.js.snap similarity index 100% rename from .framework/react/frontend/src/tests/item/__snapshots__/CommentInput.test.js.snap rename to frontend/src/tests/item/__snapshots__/CommentInput.test.js.snap diff --git a/.framework/react/frontend/start.sh b/frontend/start.sh similarity index 100% rename from .framework/react/frontend/start.sh rename to frontend/start.sh diff --git a/.framework/react/frontend/yarn.lock b/frontend/yarn.lock similarity index 100% rename from .framework/react/frontend/yarn.lock rename to frontend/yarn.lock diff --git a/readme.md b/readme.md index 19f396e..3ff175d 100644 --- a/readme.md +++ b/readme.md @@ -6,7 +6,7 @@ Please find more info about each part in the relevant Readme file ([frontend](fr ## Development -When implementing a new feature or fixing a bug, please create a new pull request against `main` from a feature/bug branch and the Wilco app review it right away +When implementing a new feature or fixing a bug, please create a new pull request against `main` from a feature/bug branch and add `@vanessa-cooper` as reviewer. ## How to run in dev mode?