├── .circleci └── config.yml ├── .editorconfig ├── .envrc.sample ├── .github └── pull_request_template.md ├── .gitignore ├── .node-version ├── .python-version ├── Dockerfile ├── README.md ├── add_master_data.sh ├── api-template.yaml ├── api-with-oauth-template.yaml ├── apialarms-template.yaml ├── cloudfront-template.yaml ├── cognito-template.yaml ├── create_resource_groups.sh ├── database-template.yaml ├── database.yaml ├── deploy.sh ├── deploy_api.sh ├── deploy_api_function.py ├── deploy_cloudfront.sh ├── docker-compose.yml ├── elasticsearch-setup.py ├── elasticsearch-template.yaml ├── exec_test.py ├── fix_api.sh ├── fix_configurations.sh ├── function-template.yaml ├── function02-template.yaml ├── make_deploy_zip.py ├── misc ├── delete_all_items.py └── topics.json ├── package-lock.json ├── package.json ├── packaging.sh ├── permission-template.yaml ├── requirements.txt ├── requirements_test.txt ├── serverless.yml ├── setup.cfg ├── src ├── common │ ├── authlete_util.py │ ├── cognito_trigger_base.py │ ├── crypto_util.py │ ├── db_util.py │ ├── decimal_encoder.py │ ├── es_util.py │ ├── exceptions.py │ ├── facebook_util.py │ ├── lambda_base.py │ ├── nft_games_info.py │ ├── no_permission_error.py │ ├── nonce_util.py │ ├── not_authorized_error.py │ ├── not_verified_user_error.py │ ├── notification_util.py │ ├── parameter_util.py │ ├── private_chain_util.py │ ├── record_not_found_error.py │ ├── response_builder.py │ ├── rsa_algorithm.py │ ├── settings.py │ ├── tag_util.py │ ├── text_sanitizer.py │ ├── time_util.py │ ├── twitter_util.py │ ├── user_util.py │ ├── web3_util.py │ └── yahoo_util.py └── handlers │ ├── applications │ └── show │ │ ├── applications_show.py │ │ └── handler.py │ ├── articles │ ├── alis_tokens │ │ └── show │ │ │ ├── articles_alis_tokens_show.py │ │ │ └── handler.py │ ├── comments │ │ └── index │ │ │ ├── articles_comments_index.py │ │ │ └── handler.py │ ├── eyecatch │ │ ├── articles_eyecatch.py │ │ └── handler.py │ ├── likes │ │ └── show │ │ │ ├── articles_likes_show.py │ │ │ └── handler.py │ ├── popular │ │ ├── articles_popular.py │ │ └── handler.py │ ├── price │ │ └── show │ │ │ ├── articles_price_show.py │ │ │ └── handler.py │ ├── recent │ │ ├── articles_recent.py │ │ └── handler.py │ ├── recommended │ │ ├── articles_recommended.py │ │ └── handler.py │ ├── show │ │ ├── articles_show.py │ │ └── handler.py │ ├── supporters │ │ └── index │ │ │ ├── articles_supporters_index.py │ │ │ └── handler.py │ └── tip_ranking │ │ ├── articles_tip_ranking.py │ │ └── handler.py │ ├── authorizer │ ├── authorizer.py │ └── handler.py │ ├── cognito_trigger │ ├── custommessage │ │ ├── custom_message.py │ │ └── handler.py │ ├── postconfirmation │ │ ├── handler.py │ │ └── post_confirmation.py │ ├── preauthentication │ │ ├── handler.py │ │ └── pre_authentication.py │ └── presignup │ │ ├── handler.py │ │ └── pre_signup.py │ ├── comments │ └── likes │ │ └── show │ │ ├── comments_likes_show.py │ │ └── handler.py │ ├── labo │ └── n │ │ ├── license_token │ │ ├── file_download_url │ │ │ ├── handler.py │ │ │ └── license_token_file_download_url.py │ │ └── file_upload_url │ │ │ ├── handler.py │ │ │ └── license_token_file_upload_url.py │ │ ├── majority_judgement │ │ ├── create │ │ │ ├── handler.py │ │ │ └── majority_judgement_create.py │ │ ├── delete_all │ │ │ ├── handler.py │ │ │ └── majority_judgement_delete_all.py │ │ └── index │ │ │ ├── handler.py │ │ │ └── majority_judgement_index.py │ │ ├── quadratic_voting │ │ ├── create │ │ │ ├── handler.py │ │ │ └── quadratic_voting_create.py │ │ └── index │ │ │ ├── handler.py │ │ │ └── quadratic_voting_index.py │ │ └── random │ │ ├── article.py │ │ └── handler.py │ ├── login │ ├── facebook │ │ ├── authorization_url │ │ │ ├── handler.py │ │ │ └── login_facebook_authorization_url.py │ │ └── index │ │ │ ├── handler.py │ │ │ └── login_facebook_index.py │ ├── line │ │ ├── authorize_request │ │ │ ├── handler.py │ │ │ └── login_line_authorize_request.py │ │ └── authorize_url │ │ │ ├── handler.py │ │ │ └── login_line_authorize_url.py │ ├── twitter │ │ ├── authorization_url │ │ │ ├── handler.py │ │ │ └── login_twitter_authorization_url.py │ │ └── index │ │ │ ├── handler.py │ │ │ └── login_twitter_index.py │ └── yahoo │ │ ├── authorization_url │ │ ├── handler.py │ │ └── login_yahoo_authorization_url.py │ │ └── index │ │ ├── handler.py │ │ └── login_yahoo_index.py │ ├── me │ ├── allowed_applications │ │ ├── delete │ │ │ ├── handler.py │ │ │ └── me_allowed_applications_delete.py │ │ └── index │ │ │ ├── handler.py │ │ │ └── me_allowed_applications_index.py │ ├── applications │ │ ├── create │ │ │ ├── handler.py │ │ │ └── me_applications_create.py │ │ ├── delete │ │ │ ├── handler.py │ │ │ └── me_applications_delete.py │ │ ├── index │ │ │ ├── handler.py │ │ │ └── me_applications_index.py │ │ ├── show │ │ │ ├── handler.py │ │ │ └── me_applications_show.py │ │ └── update │ │ │ ├── handler.py │ │ │ └── me_applications_update.py │ ├── articles │ │ ├── comments │ │ │ ├── create │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_comments_create.py │ │ │ ├── likes │ │ │ │ └── index │ │ │ │ │ ├── handler.py │ │ │ │ │ └── me_articles_comments_likes_index.py │ │ │ └── reply │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_comments_reply.py │ │ ├── content_edit_histories │ │ │ └── index │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_content_edit_histories_index.py │ │ ├── drafts │ │ │ ├── article_id │ │ │ │ └── create │ │ │ │ │ ├── handler.py │ │ │ │ │ └── me_articles_drafts_article_id_create.py │ │ │ ├── body │ │ │ │ └── update │ │ │ │ │ ├── handler.py │ │ │ │ │ └── me_articles_drafts_body_update.py │ │ │ ├── create │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_drafts_create.py │ │ │ ├── delete │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_drafts_delete.py │ │ │ ├── index │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_drafts_index.py │ │ │ ├── publish │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_drafts_publish.py │ │ │ ├── publish_with_header │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_drafts_publish_with_header.py │ │ │ ├── show │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_drafts_show.py │ │ │ ├── title │ │ │ │ └── update │ │ │ │ │ ├── handler.py │ │ │ │ │ └── me_articles_drafts_title_update.py │ │ │ └── update │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_drafts_update.py │ │ ├── fraud │ │ │ └── create │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_fraud_create.py │ │ ├── image_upload_url │ │ │ └── show │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_image_upload_url_show.py │ │ ├── images │ │ │ └── create │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_images_create.py │ │ ├── like │ │ │ ├── create │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_like_create.py │ │ │ └── show │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_like_show.py │ │ ├── public │ │ │ ├── body │ │ │ │ └── update │ │ │ │ │ ├── handler.py │ │ │ │ │ └── me_articles_public_body_update.py │ │ │ ├── edit │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_public_edit.py │ │ │ ├── index │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_public_index.py │ │ │ ├── republish │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_public_republish.py │ │ │ ├── republish_with_header │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_public_republish_with_header.py │ │ │ ├── show │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_public_show.py │ │ │ ├── title │ │ │ │ └── update │ │ │ │ │ ├── handler.py │ │ │ │ │ └── me_articles_public_title_update.py │ │ │ ├── unpublish │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_public_unpublish.py │ │ │ └── update │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_public_update.py │ │ ├── purchase │ │ │ └── create │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_purchase_create.py │ │ ├── purchased │ │ │ ├── article_ids │ │ │ │ └── index │ │ │ │ │ ├── handler.py │ │ │ │ │ └── me_articles_purchased_article_ids_index.py │ │ │ ├── index │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_purchased_index.py │ │ │ └── show │ │ │ │ ├── handler.py │ │ │ │ └── me_articles_purchased_show.py │ │ └── pv │ │ │ └── create │ │ │ ├── handler.py │ │ │ └── me_articles_pv_create.py │ ├── comments │ │ ├── delete │ │ │ ├── handler.py │ │ │ └── me_comments_delete.py │ │ └── likes │ │ │ └── create │ │ │ ├── handler.py │ │ │ └── me_comments_likes_create.py │ ├── configurations │ │ ├── mute_users │ │ │ ├── add │ │ │ │ ├── handler.py │ │ │ │ └── me_configurations_mute_users_add.py │ │ │ ├── delete │ │ │ │ ├── handler.py │ │ │ │ └── me_configurations_mute_users_delete.py │ │ │ └── index │ │ │ │ ├── handler.py │ │ │ │ └── me_configurations_mute_users_index.py │ │ └── wallet │ │ │ ├── add │ │ │ ├── handler.py │ │ │ └── me_configurations_wallet_add.py │ │ │ └── show │ │ │ ├── handler.py │ │ │ └── me_configurations_wallet_show.py │ ├── external_provider_user │ │ └── create │ │ │ ├── handler.py │ │ │ └── me_external_provider_user_create.py │ ├── info │ │ ├── first_experiences │ │ │ └── update │ │ │ │ ├── handler.py │ │ │ │ └── me_info_first_experiences_update.py │ │ ├── icon │ │ │ └── create │ │ │ │ ├── handler.py │ │ │ │ └── me_info_icon_create.py │ │ ├── show │ │ │ ├── handler.py │ │ │ └── me_info_show.py │ │ └── update │ │ │ ├── handler.py │ │ │ └── me_info_update.py │ ├── notifications │ │ └── index │ │ │ ├── handler.py │ │ │ └── me_notifications_index.py │ ├── unread_notification_managers │ │ ├── show │ │ │ ├── handler.py │ │ │ └── me_unread_notification_managers_show.py │ │ └── update │ │ │ ├── handler.py │ │ │ └── me_unread_notification_managers_update.py │ ├── users │ │ └── fraud │ │ │ └── create │ │ │ ├── handler.py │ │ │ └── me_users_fraud_create.py │ └── wallet │ │ ├── allowance │ │ └── show │ │ │ ├── handler.py │ │ │ └── me_wallet_allowance_show.py │ │ ├── balance │ │ ├── handler.py │ │ └── me_wallet_balance.py │ │ ├── distributed_tokens │ │ └── show │ │ │ ├── handler.py │ │ │ └── me_wallet_distributed_tokens_show.py │ │ ├── nonce │ │ └── show │ │ │ ├── handler.py │ │ │ └── me_wallet_nonce_show.py │ │ ├── tip │ │ ├── handler.py │ │ └── me_wallet_tip.py │ │ └── token │ │ ├── allhistories │ │ └── create │ │ │ ├── handler.py │ │ │ └── me_wallet_token_allhistories_create.py │ │ ├── histories │ │ └── index │ │ │ ├── handler.py │ │ │ └── me_wallet_token_histories_index.py │ │ └── send │ │ ├── handler.py │ │ └── me_wallet_token_send.py │ ├── search │ ├── articles │ │ ├── handler.py │ │ └── search_articles.py │ ├── tags │ │ ├── handler.py │ │ └── search_tags.py │ ├── tags_count │ │ ├── handler.py │ │ └── search_tags_count.py │ └── users │ │ ├── handler.py │ │ └── search_users.py │ ├── sign_up │ └── line │ │ └── authorize_url │ │ ├── handler.py │ │ └── sign_up_line_authorize_url.py │ ├── topics │ ├── crypto │ │ └── ranking │ │ │ └── index │ │ │ ├── handler.py │ │ │ └── topics_crypto_ranking_index.py │ ├── game │ │ └── nft_games │ │ │ ├── ranking │ │ │ └── index │ │ │ │ ├── handler.py │ │ │ │ └── topics_game_nft_games_ranking_index.py │ │ │ └── show │ │ │ ├── handler.py │ │ │ └── topics_game_nft_games_show.py │ └── index │ │ ├── handler.py │ │ └── topics_index.py │ ├── users │ ├── articles │ │ ├── popular │ │ │ ├── handler.py │ │ │ └── users_articles_popular.py │ │ └── public │ │ │ ├── handler.py │ │ │ └── users_articles_public.py │ ├── info │ │ └── show │ │ │ ├── handler.py │ │ │ └── users_info_show.py │ └── wallet │ │ └── address │ │ └── show │ │ ├── handler.py │ │ └── users_wallet_address_show.py │ └── wallet │ └── bridge_information │ └── show │ ├── handler.py │ └── wallet_bridge_information_show.py ├── swagger ├── custom-auth-swagger.yaml └── swagger.yaml └── tests ├── common ├── test_authlete_util.py ├── test_cognito_trigger_base.py ├── test_crypto_util.py ├── test_db_util.py ├── test_es_util.py ├── test_facebook_util.py ├── test_lambda_base.py ├── test_nonce_util.py ├── test_notification_util.py ├── test_parameter_util.py ├── test_private_chain_util.py ├── test_tag_util.py ├── test_text_sanitizer.py ├── test_time_util.py ├── test_twitter_util.py ├── test_user_util.py ├── test_web3_util.py └── test_yahoo_util.py ├── handlers ├── applications │ └── show │ │ └── test_applications_show.py ├── articles │ ├── alis_tokens │ │ └── show │ │ │ └── test_articles_alis_tokens_show.py │ ├── comments │ │ └── index │ │ │ └── test_articles_comments_index.py │ ├── eyecatch │ │ └── test_articles_eyecatch.py │ ├── likes │ │ └── show │ │ │ └── test_articles_likes_show.py │ ├── popular │ │ └── test_articles_popular.py │ ├── price │ │ └── show │ │ │ └── test_articles_price_show.py │ ├── recent │ │ └── test_articles_recent.py │ ├── recommended │ │ └── test_articles_recommended.py │ ├── show │ │ └── test_articles_show.py │ ├── supporters │ │ └── index │ │ │ └── test_articles_supporters_index.py │ └── tip_ranking │ │ └── test_articles_tip_ranking.py ├── authorizer │ └── test_authorizer.py ├── cognito_trigger │ ├── custommessage │ │ └── test_custom_message.py │ ├── postconfirmation │ │ └── test_post_confirmation.py │ ├── preauthentication │ │ └── test_pre_authentication.py │ └── presignup │ │ └── test_pre_signup.py ├── comments │ └── likes │ │ └── show │ │ └── test_comments_likes_show.py ├── login │ ├── facebook │ │ ├── authorization_url │ │ │ └── test_login_facebook_authorization_url.py │ │ └── index │ │ │ └── test_login_facebook_index.py │ ├── line │ │ ├── authorize_request │ │ │ └── test_login_line_authorize_request.py │ │ └── authorize_url │ │ │ └── test_login_line_authorize_url.py │ ├── twitter │ │ ├── authorization_url │ │ │ └── test_login_twitter_authorization_url.py │ │ └── index │ │ │ └── test_login_twitter_index.py │ └── yahoo │ │ ├── authorization_url │ │ └── test_login_yahoo_authorization_url.py │ │ └── index │ │ └── test_login_yahoo_index.py ├── me │ ├── allowed_applications │ │ ├── delete │ │ │ └── test_me_allowed_applications_delete.py │ │ └── index │ │ │ └── test_me_allowed_applications_index.py │ ├── applications │ │ ├── create │ │ │ └── test_me_applications_create.py │ │ ├── delete │ │ │ └── test_me_applications_delete.py │ │ ├── index │ │ │ └── test_me_applications_index.py │ │ ├── show │ │ │ └── test_me_applications_show.py │ │ └── update │ │ │ └── test_me_applications_update.py │ ├── articles │ │ ├── comments │ │ │ ├── create │ │ │ │ └── test_me_articles_comments_create.py │ │ │ ├── likes │ │ │ │ └── index │ │ │ │ │ └── test_me_articles_comments_likes_index.py │ │ │ └── reply │ │ │ │ └── test_me_articles_comments_reply.py │ │ ├── content_edit_histories │ │ │ └── index │ │ │ │ └── test_me_articles_content_edit_histories_index.py │ │ ├── drafts │ │ │ ├── article_id │ │ │ │ └── create │ │ │ │ │ └── test_me_article_drafts_article_id_create.py │ │ │ ├── body │ │ │ │ └── update │ │ │ │ │ └── test_me_articles_drafts_body_update.py │ │ │ ├── create │ │ │ │ └── test_me_article_drafts_create.py │ │ │ ├── delete │ │ │ │ └── test_me_articles_drafts_delete.py │ │ │ ├── index │ │ │ │ └── test_me_article_drafts_index.py │ │ │ ├── publish │ │ │ │ └── test_me_articles_drafts_publish.py │ │ │ ├── publish_with_header │ │ │ │ └── test_me_articles_drafts_publish_with_header.py │ │ │ ├── show │ │ │ │ └── test_me_articles_drafts_show.py │ │ │ ├── title │ │ │ │ └── update │ │ │ │ │ └── test_me_articles_drafts_title_update.py │ │ │ └── update │ │ │ │ └── test_me_articles_drafts_update.py │ │ ├── fraud │ │ │ └── create │ │ │ │ └── test_me_articles_fraud_create.py │ │ ├── image_upload_url │ │ │ └── show │ │ │ │ └── test_me_articles_image_upload_url_show.py │ │ ├── images │ │ │ └── create │ │ │ │ └── test_me_articles_images_create.py │ │ ├── like │ │ │ ├── create │ │ │ │ └── test_me_articles_like_create.py │ │ │ └── show │ │ │ │ └── test_me_articles_like_show.py │ │ ├── public │ │ │ ├── body │ │ │ │ └── update │ │ │ │ │ └── test_me_articles_public_body_update.py │ │ │ ├── edit │ │ │ │ └── test_me_articles_public_edit.py │ │ │ ├── index │ │ │ │ └── test_me_articles_public_index.py │ │ │ ├── republish │ │ │ │ └── test_me_articles_public_republish.py │ │ │ ├── republish_with_header │ │ │ │ └── test_me_articles_public_republish_with_header.py │ │ │ ├── show │ │ │ │ └── test_me_articles_public_show.py │ │ │ ├── title │ │ │ │ └── update │ │ │ │ │ └── test_me_articles_public_title_update.py │ │ │ ├── unpublish │ │ │ │ └── test_me_articles_public_unpublish.py │ │ │ └── update │ │ │ │ └── test_me_articles_public_update.py │ │ ├── purchase │ │ │ └── create │ │ │ │ └── test_me_articles_purchase_create.py │ │ ├── purchased │ │ │ ├── article_ids │ │ │ │ └── index │ │ │ │ │ └── test_me_articles_purchased_article_ids_index.py │ │ │ ├── index │ │ │ │ └── test_me_articles_purchased_index.py │ │ │ └── show │ │ │ │ └── test_me_articles_purchased_show.py │ │ └── pv │ │ │ └── create │ │ │ └── test_me_articles_pv_create.py │ ├── comments │ │ ├── delete │ │ │ └── test_me_comments_delete.py │ │ └── likes │ │ │ └── create │ │ │ └── test_me_comments_likes_create.py │ ├── configurations │ │ ├── mute_users │ │ │ ├── add │ │ │ │ └── test_me_configurations_mute_users_add.py │ │ │ ├── delete │ │ │ │ └── test_me_configurations_mute_users_delete.py │ │ │ └── index │ │ │ │ └── test_me_configurations_mute_users_index.py │ │ └── wallet │ │ │ ├── add │ │ │ └── test_me_configurations_wallet_add.py │ │ │ └── show │ │ │ └── test_me_configurations_wallet_show.py │ ├── external_provider_user │ │ └── create │ │ │ └── test_me_external_provider_user_create.py │ ├── info │ │ ├── first_experiences │ │ │ └── update │ │ │ │ └── test_me_info_first_experiences_update.py │ │ ├── icon │ │ │ └── create │ │ │ │ └── test_me_info_icon_create.py │ │ ├── show │ │ │ └── test_me_info_show.py │ │ └── update │ │ │ └── test_me_info_update.py │ ├── notifications │ │ └── index │ │ │ └── test_me_notifications_index.py │ ├── unread_notification_managers │ │ ├── show │ │ │ └── test_me_unread_notification_managers_show.py │ │ └── update │ │ │ └── test_me_unread_notification_managers_update.py │ ├── users │ │ └── fraud │ │ │ └── create │ │ │ └── test_me_users_fraud_create.py │ └── wallet │ │ ├── allowance │ │ └── show │ │ │ └── test_me_wallet_allowance_show.py │ │ ├── balance │ │ ├── README.md │ │ └── test_me_wallet_blance.py │ │ ├── distributed_tokens │ │ └── show │ │ │ └── test_me_wallet_distributed_tokens_show.py │ │ ├── nonce │ │ └── show │ │ │ └── test_me_wallet_nonce_show.py │ │ ├── tip │ │ ├── README.md │ │ └── test_me_wallet_tip.py │ │ └── token │ │ ├── allhistories │ │ └── create │ │ │ └── test_me_wallet_token_allhistories_create.py │ │ ├── histories │ │ └── index │ │ │ └── test_me_wallet_token_histories_index.py │ │ └── send │ │ └── test_me_wallet_token_send.py ├── search │ ├── articles │ │ └── test_search_articles.py │ ├── tags │ │ └── test_search_tags.py │ ├── tags_count │ │ └── test_search_tags_count.py │ └── users │ │ └── test_search_users.py ├── sign_up │ └── line │ │ └── authorize_url │ │ └── test_sign_up_line_authorize_url.py ├── topics │ ├── crypto │ │ └── ranking │ │ │ └── index │ │ │ └── test_crypto_ranking_index.py │ ├── game │ │ └── nft_games │ │ │ ├── ranking │ │ │ └── index │ │ │ │ └── test_topics_game_nft_games_ranking_index.py │ │ │ └── show │ │ │ └── test_topics_game_nft_games_show.py │ └── index │ │ └── test_topics_index.py ├── users │ ├── articles │ │ ├── popular │ │ │ └── test_users_articles_popular.py │ │ └── public │ │ │ └── test_users_articles_public.py │ ├── info │ │ └── show │ │ │ └── test_users_info_show.py │ └── wallet │ │ └── address │ │ └── show │ │ └── test_users_wallet_address_show.py └── wallet │ └── bridge_information │ └── show │ └── test_wallet_bridge_information_show.py └── tests_common ├── tests_es_util.py └── tests_util.py /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | 8 | [*.py] 9 | indent_style = space 10 | indent_size = 4 11 | -------------------------------------------------------------------------------- /.envrc.sample: -------------------------------------------------------------------------------- 1 | # --- ALIS --- 2 | 3 | ## ALISのアプリケーションID 4 | ## すべてのサブシステムで同じ値を使う必要がある 5 | export ALIS_APP_ID=foobar 6 | 7 | # CloudFormationで生成されたAPI GatewayのAPI ID 8 | # スクリプトから各APIに事後処理を行うために使用する 9 | # 現状、SAMがIAM認証をサポートしていないため 10 | # TODO: SAMがIAM認証をサポートしたらteplate.yamlに集約してこの値とスクリプトを除却する 11 | # - see: https://github.com/awslabs/serverless-application-model/issues/248 12 | export SERVERLESS_REST_API_ID=pe6odilrre 13 | export SERVERLESS_REST_API_WITH_OAUTH_ID=xxxxxxxxxx 14 | 15 | 16 | ## --- for Cloudfront --- 17 | 18 | # Cloudfrontリソースを生成する場合のみ必要 19 | # 本番環境では利用しないリソースなのでSSMではなくここに配置している 20 | 21 | # API Gateway IDs 22 | export FrontendApiGatewayId=xxxxxxxx 23 | export Oauth2ApiGatewayId=xxxxxxxx 24 | export Oauth2apiApiGatewayId=xxxxxxxx 25 | export LaboApiGatewayId=xxxxxxxx 26 | 27 | # ACMのCertificationのArn 28 | export AcmCertificateArn=arn:aws:acm:us-east-1:xxxxxx:xxxxxx 29 | 30 | ## --- for fix_configuration --- 31 | # プライベートチェーンのPrivateSubNetのサブネットID 32 | ## Lambda関数にVPC周りの設定を行うために `fix_configurations.sh` 内で使用する。CIには不要。 33 | ## CloudFormationでLambda関数にVPC周りの設定がサポートされていないための処置 34 | ## serverless applicationにはonVPCのfunctionが存在しなかったため、追加 35 | export PRIVATE_CHAIN_VPC_SUBNETS=subnet-xxx 36 | ## プライベートチェーンのセキュリティーグループID 37 | ## Lambda関数にVPC周りの設定を行うために `fix_configurations.sh` 内で使用する。CIには不要。 38 | ## CloudFormationでLambda関数にVPC周りの設定がサポートされていないための処置 39 | export PRIVATE_CHAIN_SECURITY_GROUPS=sg-xxx 40 | export TOKEN_HISTORY_CREATE=stagingfunction02-MeWalletTokenAllhistoriesCreate-xxx 41 | 42 | # --- AWS --- 43 | 44 | ## aws-cli 45 | ## aws-cliが依存する環境変数。 46 | ## - see: https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-environment.html 47 | ## .credentialsファイルよりもこちらの環境変数が優先される 48 | ## Cloud formationのデプロイやS3作成など重要な操作を行うため、管理者レベルの権限を持つ必要がある。 49 | export AWS_DEFAULT_REGION=ap-northeast-1 50 | export AWS_ACCESS_KEY_ID=AKIAJZCGSXXXXXXXXXX 51 | export AWS_SECRET_ACCESS_KEY=iU6/q6ls5i5pDBPxxxxxxxxxxxxxxxxxxxx 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *packaged-template.yaml 2 | *package-template.yaml 3 | venv 4 | deploy 5 | vendor-package 6 | .envrc 7 | _tmp_* 8 | src/**/__pycache__ 9 | update_function.sh 10 | 11 | # for tests 12 | tmp_tests 13 | tests/**/database.yaml 14 | tests/**/__pycache__ 15 | 16 | # editor 17 | .idea 18 | 19 | # for mac os 20 | .DS_Store 21 | 22 | # DynamoDBLocal 23 | DynamoDBLocal* 24 | third_party_licenses 25 | dynamodb_local* 26 | shared-local-instance.db 27 | LICENSE.txt 28 | README.txt 29 | 30 | # etc 31 | node_modules 32 | .serverless 33 | *.log 34 | update_function.sh 35 | tmp* 36 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | v12.4.0 2 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.9.13 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazon/aws-lambda-python:3.9.2022.07.19.14 2 | 3 | WORKDIR /workdir 4 | COPY requirements.txt ./ 5 | 6 | ENTRYPOINT ["pip", "install", "-r", "requirements.txt", "-t", "./vendor-package"] 7 | -------------------------------------------------------------------------------- /add_master_data.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | # --- Topic --- 5 | 6 | # マスタデータ用のJSONを生成 7 | cp -pf ./misc/topics.json ./_tmp_topics.json 8 | TARGET_TABLE_NAME=`aws ssm get-parameter --name ${ALIS_APP_ID}ssmTopicTableName --query "Parameter.Value" --output text` 9 | 10 | # 置換 11 | if sed --version 2>/dev/null | grep -q GNU; then 12 | # Linux の場合(for GNU) 13 | sed -i "s/Topic/${TARGET_TABLE_NAME}/" _tmp_topics.json 14 | else 15 | # Macの場合(for BSD) 16 | sed -i '' "s/Topic/${TARGET_TABLE_NAME}/" _tmp_topics.json 17 | fi 18 | 19 | # データを削除 20 | python ./misc/delete_all_items.py ${TARGET_TABLE_NAME} 21 | 22 | # データを登録 23 | aws dynamodb batch-write-item --request-items file://_tmp_topics.json 24 | -------------------------------------------------------------------------------- /create_resource_groups.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Stack IDのリストを取得(削除済みは除く) 4 | TMP=/tmp/_tmp_stack_id 5 | targets=(`aws cloudformation list-stacks | jq -r '.[][] | select(.DeletionTime==null) | .StackId' | grep ${ALIS_APP_ID}`) 6 | 7 | # リソースグループを作成 8 | for id in "${targets[@]}" 9 | do 10 | stack=`echo $id | awk -F'/' '{print $2}'` 11 | aws resource-groups create-group \ 12 | --name ${stack} \ 13 | --resource-query '{"Type": "CLOUDFORMATION_STACK_1_0", "Query": "{\"ResourceTypeFilters\":[\"AWS::AllSupported\"],\"StackIdentifier\":\"'${id}'\"}"}' 14 | done 15 | 16 | -------------------------------------------------------------------------------- /deploy_api.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ./deploy.sh function && ./deploy.sh function02 && ./deploy.sh api && ./deploy.sh permission 4 | 5 | if [ $ALIS_APP_ID = 'alis' ] || [ $ALIS_APP_ID = 'staging' ]; then 6 | ./deploy.sh apialarms 7 | fi 8 | -------------------------------------------------------------------------------- /deploy_cloudfront.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | aws cloudformation deploy \ 4 | --template-file cloudfront-template.yaml \ 5 | --stack-name ${ALIS_APP_ID}-cloudfront \ 6 | --parameter-overrides \ 7 | AlisAppId=${ALIS_APP_ID} \ 8 | ApiApiGatewayId=${SERVERLESS_REST_API_ID} \ 9 | FrontendApiGatewayId=${FrontendApiGatewayId} \ 10 | Oauth2ApiGatewayId=${Oauth2ApiGatewayId} \ 11 | Oauth2apiApiGatewayId=${Oauth2apiApiGatewayId} \ 12 | LaboApiGatewayId=${LaboApiGatewayId} \ 13 | AcmCertificateArn=${AcmCertificateArn} \ 14 | --capabilities CAPABILITY_IAM \ 15 | --no-fail-on-empty-changeset 16 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | services: 4 | localstack: 5 | image: localstack/localstack:0.11.4 6 | ports: 7 | - "4567-4583:4567-4583" 8 | - "${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}" 9 | environment: 10 | - SERVICES=dynamodb,s3 11 | - DEFAULT_REGION=ap-northeast-1 12 | - HOSTNAME=localhost 13 | - DYNAMODB_ERROR_PROBABILITY=0.0 14 | - LAMBDA_EXECUTOR=docker 15 | - LAMBDA_REMOTE_DOCKER=true 16 | - DATA_DIR=${DATA_DIR- } 17 | - PORT_WEB_UI=${PORT_WEB_UI- } 18 | - DEBUG=${DEBUG- } 19 | - DOCKER_HOST=unix:///var/run/docker.sock 20 | volumes: 21 | - "${TMPDIR:-/tmp/localstack}:/tmp/localstack" 22 | - "/var/run/docker.sock:/var/run/docker.sock" 23 | -------------------------------------------------------------------------------- /exec_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import shutil 4 | import argparse 5 | import subprocess 6 | import glob 7 | import re 8 | from distutils.dir_util import copy_tree 9 | 10 | TEST_DIR = 'tests' 11 | TEST_TMP_DIR = './tmp_tests' 12 | 13 | 14 | def exec_test(target_dir): 15 | print(target_dir) 16 | exit_status = 0 17 | try: 18 | subprocess.check_call(['green', '-vv', '--processes', '1', target_dir]) 19 | except subprocess.CalledProcessError: 20 | exit_status = 1 21 | 22 | return exit_status 23 | 24 | 25 | def copy_required_files(path): 26 | # テストの実行ディレクトリパスを取得 27 | test_dir = path[:path.rfind('/')] 28 | 29 | # テスト対象ソースを複製(対象ソースは tests 配下と同一構造の src ディレクトリ配下が対象) 30 | copy_tree(re.sub(r'^\./tmp_tests', './src', test_dir), test_dir) 31 | 32 | # 共通ライブラリを複製 33 | copy_tree('./src/common', test_dir) 34 | 35 | # 共通ライブラリを複製 36 | copy_tree('./tests/tests_common', test_dir) 37 | 38 | 39 | def set_global_env_vers(): 40 | os.environ['PRIVATE_CHAIN_EXECUTE_API_HOST'] = 'test' 41 | os.environ['PRIVATE_CHAIN_AWS_ACCESS_KEY'] = 'test' 42 | os.environ['PRIVATE_CHAIN_AWS_SECRET_ACCESS_KEY'] = 'test' 43 | 44 | 45 | def main(): 46 | parser = argparse.ArgumentParser() 47 | parser.add_argument('--ignore', help='テスト対象外のディレクトリを指定。カンマ区切りで複数指定可') 48 | parser.add_argument('--target', help='テスト対象のディレクトリを指定') 49 | args = parser.parse_args() 50 | 51 | if os.path.isdir(TEST_TMP_DIR): 52 | shutil.rmtree(TEST_TMP_DIR) 53 | 54 | # Lambdaではファイルがフラットに展開される。tests以下を汚さないためにtmpディレクトリを準備 55 | os.mkdir(TEST_TMP_DIR) 56 | copy_tree(TEST_DIR, TEST_TMP_DIR) 57 | 58 | # テスト対象外のディレクトリを除却 59 | if args.ignore is not None: 60 | ignore_dirs = args.ignore.split(',') 61 | for ignore_dir in ignore_dirs: 62 | shutil.rmtree(TEST_TMP_DIR + '/' + ignore_dir) 63 | 64 | # テスト実行のための準備 65 | set_global_env_vers() 66 | for name in glob.iglob(TEST_TMP_DIR + '/**/test_*.py', recursive=True): 67 | copy_required_files(name) 68 | 69 | # # 引数でテストするディレクトリを受け取っている場合は変数にセットする 70 | target_dir = args.target if args.target is not None else '' 71 | 72 | result = exec_test(TEST_TMP_DIR + target_dir) 73 | 74 | shutil.rmtree(TEST_TMP_DIR) 75 | sys.exit(result) 76 | 77 | 78 | main() 79 | -------------------------------------------------------------------------------- /fix_api.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Delete unnecessary Stage "Stage" 4 | # SAM's bug? https://github.com/awslabs/serverless-application-model/issues/168 5 | aws apigateway delete-stage --rest-api-id ${SERVERLESS_REST_API_ID} --stage-name Stage 6 | aws apigateway delete-stage --rest-api-id ${SERVERLESS_REST_API_WITH_OAUTH_ID} --stage-name Stage 7 | 8 | # --- 9 | 10 | # Enable logs 11 | aws apigateway update-stage --rest-api-id ${SERVERLESS_REST_API_ID} --stage-name 'api' --patch-operations op=replace,path=/*/*/logging/dataTrace,value=true 12 | aws apigateway update-stage --rest-api-id ${SERVERLESS_REST_API_ID} --stage-name 'api' --patch-operations op=replace,path=/*/*/logging/loglevel,value=INFO 13 | aws apigateway update-stage --rest-api-id ${SERVERLESS_REST_API_ID} --stage-name 'api' --patch-operations op=replace,path=/*/*/metrics/enabled,value=true 14 | aws apigateway update-stage --rest-api-id ${SERVERLESS_REST_API_WITH_OAUTH_ID} --stage-name 'oauth2api' --patch-operations op=replace,path=/*/*/logging/dataTrace,value=true 15 | aws apigateway update-stage --rest-api-id ${SERVERLESS_REST_API_WITH_OAUTH_ID} --stage-name 'oauth2api' --patch-operations op=replace,path=/*/*/logging/loglevel,value=INFO 16 | aws apigateway update-stage --rest-api-id ${SERVERLESS_REST_API_WITH_OAUTH_ID} --stage-name 'oauth2api' --patch-operations op=replace,path=/*/*/metrics/enabled,value=true 17 | -------------------------------------------------------------------------------- /fix_configurations.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | TOKEN_HISTORY_CREATE=`aws lambda list-functions --query "Functions[?contains(FunctionName, '${ALIS_APP_ID}function02-MeWalletTokenAllhistoriesCreate')].FunctionName" --output text` 4 | WALLET_NONCE=`aws lambda list-functions --query "Functions[?contains(FunctionName, '${ALIS_APP_ID}function02-MeWalletTokenAllhistoriesCreate')].FunctionName" --output text` 5 | 6 | # VPC config 7 | aws lambda update-function-configuration \ 8 | --function-name ${TOKEN_HISTORY_CREATE} \ 9 | --vpc-config SubnetIds="${PRIVATE_CHAIN_VPC_SUBNETS}",SecurityGroupIds="${PRIVATE_CHAIN_SECURITY_GROUPS}" 10 | -------------------------------------------------------------------------------- /make_deploy_zip.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import argparse 4 | import subprocess 5 | import glob 6 | 7 | # AWS Lambda へのデプロイ用ファイル(zip)を handler.py ファイル毎に作成する 8 | # 前提 9 | # pip install が完了していること(./venv/lib/python3.9/site-packages/ 配下に必要ライブラリが作成済であること) 10 | 11 | 12 | # 事前処理 13 | 14 | # デプロイディレクトリを空にする 15 | DEPLOY_PATH = os.getcwd() + '/deploy/' 16 | if os.path.exists(DEPLOY_PATH): 17 | shutil.rmtree(DEPLOY_PATH) 18 | os.makedirs(DEPLOY_PATH) 19 | 20 | # 引数を取得 21 | parser = argparse.ArgumentParser() 22 | parser.add_argument('--target', help='パッケージングする関数のhandlerへのパスを指定') 23 | args = parser.parse_args() 24 | 25 | 26 | # deploy 用 zip ファイルを作成 27 | def make_deploy_zip(zip_file_name, target_dir): 28 | # zip 作成(実行ファイルパス) 29 | exec_zip(zip_file_name, target_dir) 30 | # zip 追加(共通ライブラリ) 31 | exec_zip(zip_file_name, 'src/common') 32 | # zip 追加(venv ライブラリ) 33 | exec_zip(zip_file_name, 'vendor-package') 34 | 35 | 36 | # zip ファイル作成実行 37 | def exec_zip(zip_file_name, zip_target_dir): 38 | cmd = 'cd ' + os.getcwd() + '/' + zip_target_dir + '; zip -r ' + DEPLOY_PATH + zip_file_name + ' ./*' 39 | subprocess.check_call(cmd, shell=True) 40 | 41 | 42 | # --- メイン処理 --- 43 | 44 | 45 | # 引数でパッケージングする関数のhandlerへのパスを受け取っている場合は変数にセットする 46 | # デフォルトはすべてのリソースをパッケージング 47 | target = args.target if args.target is not None else 'src/handlers/**/handler.py' 48 | 49 | # 各 handler ファイル毎に、共通ライブラリと venv のライブラリを含めて zip ファイルを作成する 50 | for name in glob.iglob(target, recursive=True): 51 | # 実行ディレクトリパスを取得 52 | target = './' + name[:name.rfind('/')] 53 | # zip のファイル名を取得 54 | zip_file_name = target[len('./src/handlers/'):].replace('/', '_') + '.zip' 55 | # zip 作成 56 | make_deploy_zip(zip_file_name, target) 57 | -------------------------------------------------------------------------------- /misc/delete_all_items.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import boto3 3 | 4 | 5 | ################################################################# 6 | # 全レコード削除対象のテーブル名を引数に実行。 7 | # $ python delete_all_items hoge_table_name 8 | ################################################################# 9 | def main(): 10 | validate() 11 | delete_all_items(sys.argv[1]) 12 | 13 | 14 | def validate(): 15 | if len(sys.argv) <= 1: 16 | print('全レコード削除対象のテーブル名を指定してください') 17 | exit(1) 18 | 19 | print(f'{sys.argv[1]} の全レコードを削除します。よろしいですか(y/n)?') 20 | input_str = input() 21 | if input_str != 'y' and input_str != 'Y': 22 | print('処理を中断します') 23 | exit(0) 24 | 25 | 26 | def delete_all_items(table_name): 27 | dynamodb = boto3.resource('dynamodb') 28 | target_table = dynamodb.Table(table_name) 29 | target_items = scan_all_items(target_table) 30 | 31 | target_table_key_names = [k["AttributeName"] for k in target_table.key_schema] 32 | target_table_keys = [{k: v for k, v in i.items() if k in target_table_key_names} for i in target_items] 33 | with target_table.batch_writer() as batch: 34 | for key in target_table_keys: 35 | batch.delete_item(Key=key) 36 | 37 | 38 | def scan_all_items(dynamodb_table): 39 | response = dynamodb_table.scan() 40 | items = response['Items'] 41 | while 'LastEvaluatedKey' in response: 42 | response = dynamodb_table.scan(ExclusiveStartKey=response['LastEvaluatedKey']) 43 | items.extend(response['Items']) 44 | return items 45 | 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "lint": "npx cfn-lint validate", 4 | "build": "python make_deploy_zip.py --target 'src/handlers/labo/**/handler.py'", 5 | "deploy": "npx sls deploy", 6 | "bd": "npm run build && npm run deploy", 7 | "remove": "npx sls remove", 8 | "invoke": "npx sls invoke -f", 9 | "cilint": "circleci config validate -c .circleci/config.yml" 10 | }, 11 | "devDependencies": { 12 | "cfn-lint": "^1.9.7", 13 | "serverless": "^1.83.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packaging.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | rm -fr ./vendor-package/* 3 | docker image build --tag deploy-image . 4 | docker container run -it --name deploy-container deploy-image 5 | docker container cp deploy-container:/workdir/vendor-package . 6 | docker container rm deploy-container 7 | docker image rm deploy-image 8 | python make_deploy_zip.py 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aws-requests-auth==0.4.3 2 | bleach==5.0.1 3 | elasticsearch==6.4.0 4 | hashids==1.3.1 5 | hexbytes==0.2.2 6 | jinja2==3.1.2 7 | jsonschema==3.2.0 8 | pillow==9.2.0 9 | pycryptodome==3.15.0 10 | pyjwt==1.7.1 11 | pytz==2022.1 12 | pyyaml==5.4.1 13 | requests_aws4auth==1.1.2 14 | requests_oauthlib==1.3.1 15 | requests==2.28.1 16 | responses==0.21.0 17 | rfc3987==1.3.8 18 | web3==5.30.0 19 | -------------------------------------------------------------------------------- /requirements_test.txt: -------------------------------------------------------------------------------- 1 | awscli==1.25.36 2 | boto3==1.24.36 3 | freezegun==1.2.1 4 | green==2.14.1 5 | pycodestyle==2.4.0 6 | pyflakes==2.4.0 7 | responses==0.21.0 8 | six==1.16.0 9 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [pycodestyle] 2 | max-line-length = 128 3 | -------------------------------------------------------------------------------- /src/common/authlete_util.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import requests 4 | import settings 5 | from record_not_found_error import RecordNotFoundError 6 | from jsonschema import ValidationError 7 | 8 | 9 | class AuthleteUtil: 10 | @staticmethod 11 | def is_accessible_client(client_id, user_id): 12 | try: 13 | response = requests.get( 14 | settings.AUTHLETE_CLIENT_ENDPOINT + '/get/' + str(client_id), 15 | auth=(os.environ['AUTHLETE_API_KEY'], os.environ['AUTHLETE_API_SECRET']) 16 | ) 17 | 18 | if response.status_code == 404: 19 | raise RecordNotFoundError('{0} is not found.'.format(client_id)) 20 | 21 | except requests.exceptions.RequestException as err: 22 | raise Exception('Something went wrong when call Authlete API: {0}'.format(err)) 23 | 24 | developer = json.loads(response.text)['developer'] 25 | 26 | return developer == user_id 27 | 28 | # 400, 404以外はALIS上では異常な状態であるため、システムエラーとして扱い、検知対象にする 29 | @staticmethod 30 | def verify_valid_response(response, request_client_id=None): 31 | if response.status_code == 400: 32 | raise ValidationError('Please check the input parameters') 33 | if request_client_id and response.status_code == 404: 34 | raise RecordNotFoundError('{0} is not found.'.format(request_client_id)) 35 | 36 | if response.status_code not in [200, 201, 204]: 37 | raise Exception('Something went wrong when call Authlete API: {0}, {1}' 38 | .format(response.status_code, response.text)) 39 | -------------------------------------------------------------------------------- /src/common/cognito_trigger_base.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | import logging 3 | import traceback 4 | from jsonschema import ValidationError 5 | 6 | 7 | class CognitoTriggerBase(metaclass=ABCMeta): 8 | def __init__(self, event, context, dynamodb=None, s3=None, cognito=None, elasticsearch=None): 9 | self.event = event 10 | self.context = context 11 | self.dynamodb = dynamodb 12 | self.s3 = s3 13 | self.cognito = cognito 14 | self.elasticsearch = elasticsearch 15 | self.params = None 16 | self.headers = None 17 | 18 | @abstractmethod 19 | def get_schema(self): 20 | pass 21 | 22 | @abstractmethod 23 | def exec_main_proc(self): 24 | pass 25 | 26 | @abstractmethod 27 | def validate_params(self): 28 | pass 29 | 30 | def main(self): 31 | logger = logging.getLogger() 32 | logger.setLevel(logging.INFO) 33 | 34 | try: 35 | # params validation 36 | self.validate_params() 37 | 38 | # exec main process 39 | return self.exec_main_proc() 40 | except ValidationError as err: 41 | logger.fatal(err) 42 | logger.info(self.event) 43 | raise Exception(err.message) 44 | 45 | except Exception as err: 46 | logger.fatal(err) 47 | logger.info(self.event) 48 | traceback.print_exc() 49 | raise Exception('Internal server error') 50 | -------------------------------------------------------------------------------- /src/common/crypto_util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import base64 3 | from Crypto.Cipher import AES 4 | from botocore.exceptions import ClientError 5 | 6 | 7 | class CryptoUtil: 8 | 9 | @staticmethod 10 | def encrypt_password(plain_text_password, iv): 11 | salt = os.environ['LOGIN_SALT'] 12 | cipher = AES.new(salt.encode("utf8"), AES.MODE_CBC, iv) 13 | return base64.b64encode(cipher.encrypt(plain_text_password.encode("utf8"))).decode() 14 | 15 | @staticmethod 16 | def decrypt_password(byte_hash_data, iv): 17 | encrypted_data = base64.b64decode(byte_hash_data) 18 | aes_iv = base64.b64decode(iv) 19 | salt = os.environ['LOGIN_SALT'] 20 | cipher = AES.new(salt.encode("utf8"), AES.MODE_CBC, aes_iv) 21 | return cipher.decrypt(encrypted_data).decode() 22 | 23 | @staticmethod 24 | def get_external_provider_password(dynamodb, user_id): 25 | try: 26 | external_provider_user = dynamodb.Table( 27 | os.environ['EXTERNAL_PROVIDER_USERS_TABLE_NAME']).get_item(Key={ 28 | 'external_provider_user_id': user_id 29 | }).get('Item') 30 | return CryptoUtil.decrypt_password( 31 | external_provider_user['password'].encode(), 32 | external_provider_user['iv'].encode() 33 | ) 34 | except ClientError as e: 35 | raise e 36 | -------------------------------------------------------------------------------- /src/common/decimal_encoder.py: -------------------------------------------------------------------------------- 1 | import json 2 | import decimal 3 | 4 | 5 | class DecimalEncoder(json.JSONEncoder): 6 | def default(self, o): 7 | if isinstance(o, decimal.Decimal): 8 | if o % 1 > 0: 9 | return float(o) 10 | else: 11 | return int(o) 12 | return super(DecimalEncoder, self).default(o) 13 | -------------------------------------------------------------------------------- /src/common/no_permission_error.py: -------------------------------------------------------------------------------- 1 | class NoPermissionError(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /src/common/nonce_util.py: -------------------------------------------------------------------------------- 1 | import string 2 | import random 3 | import os 4 | import time 5 | from botocore.exceptions import ClientError 6 | 7 | 8 | class NonceUtil: 9 | @staticmethod 10 | def generate(dynamodb, expiration_minites, provider, type, length): 11 | chars = string.ascii_letters + string.digits 12 | nonce = ''.join([random.choice(chars) for i in range(length)]) 13 | expiration_time = int(time.time()) + expiration_minites*60 14 | try: 15 | nonce_table = dynamodb.Table(os.environ['NONCE_TABLE_NAME']) 16 | param = { 17 | 'nonce': nonce, 18 | 'provider': provider, 19 | 'type': type, 20 | 'expiration_time': expiration_time 21 | } 22 | nonce_table.put_item( 23 | Item=param, 24 | ConditionExpression='attribute_not_exists(nonce)' 25 | ) 26 | except ClientError as e: 27 | raise e 28 | return nonce 29 | 30 | @staticmethod 31 | def verify(dynamodb, nonce, provider, type): 32 | try: 33 | nonce_table = dynamodb.Table(os.environ['NONCE_TABLE_NAME']) 34 | result = nonce_table.get_item(Key={ 35 | 'nonce': nonce 36 | }).get('Item') 37 | 38 | if result is None: 39 | return False 40 | 41 | if result['provider'] != provider or result['type'] != type: 42 | return False 43 | return True 44 | except ClientError as e: 45 | raise e 46 | -------------------------------------------------------------------------------- /src/common/not_authorized_error.py: -------------------------------------------------------------------------------- 1 | class NotAuthorizedError(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /src/common/not_verified_user_error.py: -------------------------------------------------------------------------------- 1 | class NotVerifiedUserError(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /src/common/notification_util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | import settings 5 | from time_util import TimeUtil 6 | 7 | 8 | class NotificationUtil: 9 | 10 | @staticmethod 11 | def notify_article_comment(dynamodb, article_info, comment, target_user_id, comment_type): 12 | if comment_type not in settings.COMMENT_NOTIFICATION_TYPES: 13 | raise ValueError('Invalid comment type ' + comment_type) 14 | 15 | notification_table = dynamodb.Table(os.environ['NOTIFICATION_TABLE_NAME']) 16 | 17 | notification_id = '-'.join([comment_type, target_user_id, comment['comment_id']]) 18 | 19 | notification_table.put_item(Item={ 20 | 'notification_id': notification_id, 21 | 'user_id': target_user_id, 22 | 'article_id': article_info['article_id'], 23 | 'article_user_id': article_info['user_id'], 24 | 'article_title': article_info['title'], 25 | 'acted_user_id': comment['user_id'], 26 | 'sort_key': TimeUtil.generate_sort_key(), 27 | 'type': comment_type, 28 | 'created_at': int(time.time()) 29 | }) 30 | 31 | @staticmethod 32 | def update_unread_notification_manager(dynamodb, user_id): 33 | unread_notification_manager_table = dynamodb.Table(os.environ['UNREAD_NOTIFICATION_MANAGER_TABLE_NAME']) 34 | 35 | unread_notification_manager_table.update_item( 36 | Key={'user_id': user_id}, 37 | UpdateExpression='set unread = :unread', 38 | ExpressionAttributeValues={':unread': True} 39 | ) 40 | -------------------------------------------------------------------------------- /src/common/parameter_util.py: -------------------------------------------------------------------------------- 1 | from jsonschema import ValidationError 2 | 3 | 4 | class ParameterUtil: 5 | @staticmethod 6 | def cast_parameter_to_int(params, schema): 7 | properties = schema['properties'] 8 | 9 | for key, value in params.items(): 10 | if properties.get(key) is None: 11 | continue 12 | 13 | if properties[key]['type'] == 'integer' and value.isdigit(): 14 | params[key] = int(value) 15 | 16 | @staticmethod 17 | def validate_array_unique(items, key, case_insensitive=False): 18 | if len(items) != len(set(items)): 19 | raise ValidationError("{key} must be unique".format(key=key)) 20 | 21 | if case_insensitive: 22 | lower_items = [item.lower() for item in items] 23 | 24 | if len(lower_items) != len(set(lower_items)): 25 | raise ValidationError("{key} must be unique(case-insensitive)".format(key=key)) 26 | 27 | @staticmethod 28 | def validate_price_params(params_price): 29 | if params_price is not None: 30 | try: 31 | params_price = int(params_price) 32 | except ValueError: 33 | raise ValidationError('Price must be integer') 34 | 35 | # check price value is not decimal 36 | price = params_price / 10 ** 18 37 | if price.is_integer() is False: 38 | raise ValidationError('Decimal value is not allowed') 39 | return True 40 | -------------------------------------------------------------------------------- /src/common/record_not_found_error.py: -------------------------------------------------------------------------------- 1 | class RecordNotFoundError(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /src/common/response_builder.py: -------------------------------------------------------------------------------- 1 | import json 2 | from decimal_encoder import DecimalEncoder 3 | 4 | 5 | class ResponseBuilder: 6 | @staticmethod 7 | def response(status_code, body): 8 | return { 9 | 'statusCode': status_code, 10 | 'body': json.dumps(body, cls=DecimalEncoder) 11 | } 12 | -------------------------------------------------------------------------------- /src/common/rsa_algorithm.py: -------------------------------------------------------------------------------- 1 | # 2 | # PyCryptoからPyCryptodomeへ移行する際、pyjwtの仕様上、うまく寄せられない部分があったので、 3 | # 本クラスを独自クラスとして切り出し、差分を吸収 4 | # 一応、pyjwtにPRを出している: https://github.com/jpadilla/pyjwt/pull/434 5 | # 6 | # 元クラス: https://github.com/jpadilla/pyjwt/blob/1.7.1/jwt/contrib/algorithms/pycrypto.py 7 | # タスク: https://alismedia.atlassian.net/browse/ALIS-3996 8 | # 9 | import Crypto.Hash.SHA256 10 | import Crypto.Hash.SHA384 11 | import Crypto.Hash.SHA512 12 | from Crypto.PublicKey import RSA 13 | from Crypto.Signature import PKCS1_v1_5 14 | 15 | from jwt.algorithms import Algorithm 16 | from jwt.compat import string_types, text_type 17 | 18 | 19 | class RSAAlgorithm(Algorithm): 20 | """ 21 | Performs signing and verification operations using 22 | RSASSA-PKCS-v1_5 and the specified hash function. 23 | 24 | This class requires PyCrypto package to be installed. 25 | 26 | This is based off of the implementation in PyJWT 0.3.2 27 | """ 28 | SHA256 = Crypto.Hash.SHA256 29 | SHA384 = Crypto.Hash.SHA384 30 | SHA512 = Crypto.Hash.SHA512 31 | 32 | def __init__(self, hash_alg): 33 | self.hash_alg = hash_alg 34 | 35 | def prepare_key(self, key): 36 | 37 | # 差分はこの2行のコメントアウトのみ 38 | # if isinstance(key, RSA._RSAobj): 39 | # return key 40 | 41 | if isinstance(key, string_types): 42 | if isinstance(key, text_type): 43 | key = key.encode('utf-8') 44 | 45 | key = RSA.importKey(key) 46 | else: 47 | raise TypeError('Expecting a PEM- or RSA-formatted key.') 48 | 49 | return key 50 | 51 | def sign(self, msg, key): 52 | return PKCS1_v1_5.new(key).sign(self.hash_alg.new(msg)) 53 | 54 | def verify(self, msg, key, sig): 55 | return PKCS1_v1_5.new(key).verify(self.hash_alg.new(msg), sig) 56 | -------------------------------------------------------------------------------- /src/common/time_util.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class TimeUtil: 5 | @staticmethod 6 | def generate_sort_key(): 7 | return int(time.time() * 1000000) 8 | -------------------------------------------------------------------------------- /src/handlers/applications/show/applications_show.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import requests 4 | import settings 5 | from jsonschema import validate 6 | from parameter_util import ParameterUtil 7 | from authlete_util import AuthleteUtil 8 | from lambda_base import LambdaBase 9 | 10 | 11 | class ApplicationShow(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'client_id': settings.parameters['oauth_client']['client_id'] 17 | }, 18 | 'required': ['client_id'] 19 | } 20 | 21 | def validate_params(self): 22 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 23 | validate(self.params, self.get_schema()) 24 | 25 | def exec_main_proc(self): 26 | try: 27 | response = requests.get( 28 | settings.AUTHLETE_CLIENT_ENDPOINT + '/get/' + str(self.params['client_id']), 29 | auth=(os.environ['AUTHLETE_API_KEY'], os.environ['AUTHLETE_API_SECRET']) 30 | ) 31 | except requests.exceptions.RequestException as err: 32 | raise Exception('Something went wrong when call Authlete API: {0}'.format(err)) 33 | 34 | AuthleteUtil.verify_valid_response(response, request_client_id=self.params['client_id']) 35 | 36 | response_dict = json.loads(response.text) 37 | return_body_dict = { 38 | 'clientName': response_dict['clientName'], 39 | 'description': response_dict.get('description') 40 | } 41 | 42 | return { 43 | 'statusCode': 200, 44 | 'body': json.dumps(return_body_dict) 45 | } 46 | -------------------------------------------------------------------------------- /src/handlers/applications/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from applications_show import ApplicationShow 3 | 4 | 5 | def lambda_handler(event, context): 6 | applications_show = ApplicationShow(event=event, context=context) 7 | return applications_show.main() 8 | -------------------------------------------------------------------------------- /src/handlers/articles/alis_tokens/show/articles_alis_tokens_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | import settings 5 | from db_util import DBUtil 6 | from lambda_base import LambdaBase 7 | from jsonschema import validate, ValidationError 8 | from decimal_encoder import DecimalEncoder 9 | 10 | 11 | class ArticlesAlisTokensShow(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'article_id': settings.parameters['article_id'] 17 | }, 18 | 'required': ['article_id'] 19 | } 20 | 21 | def validate_params(self): 22 | # single 23 | params = self.event.get('pathParameters') 24 | 25 | if params is None: 26 | raise ValidationError('pathParameters is required') 27 | 28 | validate(params, self.get_schema()) 29 | # relation 30 | DBUtil.validate_article_existence( 31 | self.dynamodb, 32 | params['article_id'], 33 | status='public' 34 | ) 35 | 36 | def exec_main_proc(self): 37 | article_evaluated_manage_table = self.dynamodb.Table(os.environ['ARTICLE_EVALUATED_MANAGE_TABLE_NAME']) 38 | article_alis_token_table = self.dynamodb.Table(os.environ['ARTICLE_ALIS_TOKEN_TABLE_NAME']) 39 | article_evaluated_manage = article_evaluated_manage_table.get_item(Key={'type': 'alistoken'}) 40 | 41 | if article_evaluated_manage.get('Item') is None: 42 | return { 43 | 'statusCode': 200, 44 | 'body': json.dumps({'article_id': self.params['article_id'], 'alis_token': 0}) 45 | } 46 | 47 | responce = article_alis_token_table.get_item( 48 | Key={ 49 | 'evaluated_at': article_evaluated_manage['Item']['active_evaluated_at'], 50 | 'article_id': self.params['article_id'] 51 | } 52 | ) 53 | 54 | if responce.get('Item') is None: 55 | return { 56 | 'statusCode': 200, 57 | 'body': json.dumps({'article_id': self.params['article_id'], 'alis_token': 0}) 58 | } 59 | 60 | return { 61 | 'statusCode': 200, 62 | 'body': json.dumps(responce['Item'], cls=DecimalEncoder) 63 | } 64 | -------------------------------------------------------------------------------- /src/handlers/articles/alis_tokens/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from articles_alis_tokens_show import ArticlesAlisTokensShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | articles_alis_tokens_show = ArticlesAlisTokensShow(event, context, dynamodb) 10 | return articles_alis_tokens_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/articles/comments/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from articles_comments_index import ArticlesCommentsIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | articles_comments_index = ArticlesCommentsIndex(event=event, context=context, dynamodb=dynamodb) 10 | return articles_comments_index.main() 11 | -------------------------------------------------------------------------------- /src/handlers/articles/eyecatch/articles_eyecatch.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import settings 4 | 5 | from jsonschema import validate 6 | from decimal_encoder import DecimalEncoder 7 | from lambda_base import LambdaBase 8 | 9 | 10 | class ArticlesEyecatch(LambdaBase): 11 | def get_schema(self): 12 | return { 13 | 'type': 'object', 14 | 'properties': { 15 | 'topic': settings.parameters['topic'] 16 | }, 17 | 'required': ['topic'] 18 | } 19 | 20 | def validate_params(self): 21 | validate(self.params, self.get_schema()) 22 | 23 | def exec_main_proc(self): 24 | screened_article_table = self.dynamodb.Table(os.environ['SCREENED_ARTICLE_TABLE_NAME']) 25 | eyecatch_articles = screened_article_table.get_item(Key={'article_type': 'eyecatch'}).get('Item') 26 | 27 | if not eyecatch_articles \ 28 | or not eyecatch_articles.get('articles') \ 29 | or not eyecatch_articles.get('articles').get(self.params['topic']): 30 | items = [] 31 | return { 32 | 'statusCode': 200, 33 | 'body': json.dumps({'Items': items}) 34 | } 35 | 36 | items = [self.__get_public_article(article_id) for article_id in 37 | eyecatch_articles.get('articles').get(self.params['topic'])] 38 | items = [item for item in items if item is not None] 39 | 40 | return { 41 | 'statusCode': 200, 42 | 'body': json.dumps({'Items': items}, cls=DecimalEncoder) 43 | } 44 | 45 | def __get_public_article(self, article_id): 46 | article_info_table = self.dynamodb.Table(os.environ['ARTICLE_INFO_TABLE_NAME']) 47 | article_info = article_info_table.get_item(Key={'article_id': article_id}).get('Item') 48 | 49 | if not article_info or not article_info['status'] == 'public': 50 | return None 51 | 52 | return article_info 53 | -------------------------------------------------------------------------------- /src/handlers/articles/eyecatch/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from articles_eyecatch import ArticlesEyecatch 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | articles_eyecatch = ArticlesEyecatch(event, context, dynamodb=dynamodb) 10 | return articles_eyecatch.main() 11 | -------------------------------------------------------------------------------- /src/handlers/articles/likes/show/articles_likes_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | import settings 5 | from db_util import DBUtil 6 | from lambda_base import LambdaBase 7 | from jsonschema import validate, ValidationError 8 | from boto3.dynamodb.conditions import Key 9 | from decimal_encoder import DecimalEncoder 10 | 11 | 12 | class ArticlesLikesShow(LambdaBase): 13 | def get_schema(self): 14 | return { 15 | 'type': 'object', 16 | 'properties': { 17 | 'article_id': settings.parameters['article_id'] 18 | }, 19 | 'required': ['article_id'] 20 | } 21 | 22 | def validate_params(self): 23 | # single 24 | if self.event.get('pathParameters') is None: 25 | raise ValidationError('pathParameters is required') 26 | validate(self.event.get('pathParameters'), self.get_schema()) 27 | # relation 28 | 29 | DBUtil.validate_article_existence( 30 | self.dynamodb, 31 | self.event['pathParameters']['article_id'], 32 | status='public' 33 | ) 34 | 35 | def exec_main_proc(self): 36 | query_params = { 37 | 'KeyConditionExpression': Key('article_id').eq(self.event['pathParameters']['article_id']), 38 | 'Select': 'COUNT' 39 | } 40 | article_liked_user_table = self.dynamodb.Table(os.environ['ARTICLE_LIKED_USER_TABLE_NAME']) 41 | response = article_liked_user_table.query(**query_params) 42 | 43 | return { 44 | 'statusCode': 200, 45 | 'body': json.dumps({'count': response['Count']}, cls=DecimalEncoder) 46 | } 47 | -------------------------------------------------------------------------------- /src/handlers/articles/likes/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from articles_likes_show import ArticlesLikesShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | articles_likes_get = ArticlesLikesShow(event=event, context=context, dynamodb=dynamodb) 10 | return articles_likes_get.main() 11 | -------------------------------------------------------------------------------- /src/handlers/articles/popular/articles_popular.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import settings 4 | from db_util import DBUtil 5 | from es_util import ESUtil 6 | from lambda_base import LambdaBase 7 | from jsonschema import validate 8 | from decimal_encoder import DecimalEncoder 9 | from parameter_util import ParameterUtil 10 | 11 | 12 | class ArticlesPopular(LambdaBase): 13 | def get_schema(self): 14 | return { 15 | 'type': 'object', 16 | 'properties': { 17 | 'limit': settings.parameters['limit'], 18 | 'page': settings.parameters['page'], 19 | 'topic': settings.parameters['topic'] 20 | } 21 | } 22 | 23 | def validate_params(self): 24 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 25 | 26 | validate(self.params, self.get_schema()) 27 | 28 | if self.params.get('topic'): 29 | DBUtil.validate_topic(self.dynamodb, self.params['topic']) 30 | 31 | def exec_main_proc(self): 32 | limit = int(self.params['limit']) if self.params.get('limit') else settings.articles_popular_default_limit 33 | page = int(self.params['page']) if self.params.get('page') else 1 34 | 35 | articles = ESUtil.search_popular_articles(self.elasticsearch, self.dynamodb, self.params, limit, page) 36 | 37 | response = { 38 | 'Items': articles 39 | } 40 | 41 | return { 42 | 'statusCode': 200, 43 | 'body': json.dumps(response, cls=DecimalEncoder) 44 | } 45 | -------------------------------------------------------------------------------- /src/handlers/articles/popular/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | import boto3 5 | from elasticsearch import Elasticsearch, RequestsHttpConnection 6 | from requests_aws4auth import AWS4Auth 7 | 8 | from articles_popular import ArticlesPopular 9 | 10 | dynamodb = boto3.resource('dynamodb') 11 | awsauth = AWS4Auth( 12 | os.environ['AWS_ACCESS_KEY_ID'], 13 | os.environ['AWS_SECRET_ACCESS_KEY'], 14 | os.environ['AWS_REGION'], 15 | 'es', 16 | session_token=os.environ['AWS_SESSION_TOKEN'] 17 | ) 18 | elasticsearch = Elasticsearch( 19 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 20 | http_auth=awsauth, 21 | use_ssl=True, 22 | verify_certs=True, 23 | connection_class=RequestsHttpConnection 24 | ) 25 | 26 | 27 | def lambda_handler(event, context): 28 | articles_popular = ArticlesPopular(event, context, dynamodb=dynamodb, elasticsearch=elasticsearch) 29 | return articles_popular.main() 30 | -------------------------------------------------------------------------------- /src/handlers/articles/price/show/articles_price_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | import settings 5 | from db_util import DBUtil 6 | from lambda_base import LambdaBase 7 | from jsonschema import validate 8 | from decimal_encoder import DecimalEncoder 9 | 10 | 11 | class ArticlesPriceShow(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'article_id': settings.parameters['article_id'] 17 | }, 18 | 'required': ['article_id'] 19 | } 20 | 21 | def validate_params(self): 22 | # single 23 | validate(self.params, self.get_schema()) 24 | # relation 25 | DBUtil.validate_article_existence( 26 | self.dynamodb, 27 | self.params['article_id'], 28 | status='public', 29 | is_purchased=True 30 | ) 31 | 32 | def exec_main_proc(self): 33 | params = self.event.get('pathParameters') 34 | 35 | article_info_table = self.dynamodb.Table(os.environ['ARTICLE_INFO_TABLE_NAME']) 36 | article_info = article_info_table.get_item(Key={'article_id': params['article_id']}).get('Item') 37 | 38 | if article_info is None: 39 | return { 40 | 'statusCode': 404, 41 | 'body': json.dumps({'message': 'Record Not Found'}) 42 | } 43 | 44 | response = { 45 | 'article_id': params['article_id'], 46 | 'price': article_info['price'] 47 | } 48 | 49 | return { 50 | 'statusCode': 200, 51 | 'body': json.dumps(response, cls=DecimalEncoder) 52 | } 53 | -------------------------------------------------------------------------------- /src/handlers/articles/price/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from articles_price_show import ArticlesPriceShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | articles_price_show = ArticlesPriceShow(event, context, dynamodb) 10 | return articles_price_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/articles/recent/articles_recent.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import settings 4 | from db_util import DBUtil 5 | from lambda_base import LambdaBase 6 | from jsonschema import validate 7 | from decimal_encoder import DecimalEncoder 8 | from parameter_util import ParameterUtil 9 | from es_util import ESUtil 10 | 11 | 12 | class ArticlesRecent(LambdaBase): 13 | def get_schema(self): 14 | return { 15 | 'type': 'object', 16 | 'properties': { 17 | 'limit': settings.parameters['limit'], 18 | 'page': settings.parameters['page'], 19 | 'topic': settings.parameters['topic'] 20 | } 21 | } 22 | 23 | def validate_params(self): 24 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 25 | 26 | validate(self.params, self.get_schema()) 27 | 28 | if self.params.get('topic'): 29 | DBUtil.validate_topic(self.dynamodb, self.params['topic']) 30 | 31 | def exec_main_proc(self): 32 | limit = int(self.params.get('limit')) if self.params.get('limit') is not None \ 33 | else settings.article_recent_default_limit 34 | page = int(self.params.get('page')) if self.params.get('page') is not None else 1 35 | 36 | articles = ESUtil.search_recent_articles(self.elasticsearch, self.dynamodb, self.params, limit, page) 37 | 38 | response = { 39 | 'Items': articles 40 | } 41 | 42 | return { 43 | 'statusCode': 200, 44 | 'body': json.dumps(response, cls=DecimalEncoder) 45 | } 46 | -------------------------------------------------------------------------------- /src/handlers/articles/recent/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | import boto3 5 | from articles_recent import ArticlesRecent 6 | from elasticsearch import Elasticsearch, RequestsHttpConnection 7 | from requests_aws4auth import AWS4Auth 8 | 9 | dynamodb = boto3.resource('dynamodb') 10 | awsauth = AWS4Auth( 11 | os.environ['AWS_ACCESS_KEY_ID'], 12 | os.environ['AWS_SECRET_ACCESS_KEY'], 13 | os.environ['AWS_REGION'], 14 | 'es', 15 | session_token=os.environ['AWS_SESSION_TOKEN'] 16 | ) 17 | 18 | elasticsearch = Elasticsearch( 19 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 20 | http_auth=awsauth, 21 | use_ssl=True, 22 | verify_certs=True, 23 | connection_class=RequestsHttpConnection 24 | ) 25 | 26 | 27 | def lambda_handler(event, context): 28 | articles_recent = ArticlesRecent(event, context, dynamodb=dynamodb, elasticsearch=elasticsearch) 29 | return articles_recent.main() 30 | -------------------------------------------------------------------------------- /src/handlers/articles/recommended/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from articles_recommended import ArticlesRecommended 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | articles_recommended = ArticlesRecommended(event, context, dynamodb=dynamodb) 10 | return articles_recommended.main() 11 | -------------------------------------------------------------------------------- /src/handlers/articles/show/articles_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | import settings 5 | from db_util import DBUtil 6 | from lambda_base import LambdaBase 7 | from jsonschema import validate, ValidationError 8 | from decimal_encoder import DecimalEncoder 9 | 10 | 11 | class ArticlesShow(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'article_id': settings.parameters['article_id'] 17 | }, 18 | 'required': ['article_id'] 19 | } 20 | 21 | def validate_params(self): 22 | # single 23 | params = self.event.get('pathParameters') 24 | 25 | if params is None: 26 | raise ValidationError('pathParameters is required') 27 | 28 | validate(params, self.get_schema()) 29 | # relation 30 | DBUtil.validate_article_existence( 31 | self.dynamodb, 32 | params['article_id'], 33 | status='public' 34 | ) 35 | 36 | def exec_main_proc(self): 37 | params = self.event.get('pathParameters') 38 | 39 | article_info_table = self.dynamodb.Table(os.environ['ARTICLE_INFO_TABLE_NAME']) 40 | article_content_table = self.dynamodb.Table(os.environ['ARTICLE_CONTENT_TABLE_NAME']) 41 | 42 | article_info = article_info_table.get_item(Key={'article_id': params['article_id']}).get('Item') 43 | article_content = article_content_table.get_item(Key={'article_id': params['article_id']}).get('Item') 44 | 45 | if article_info is None or article_content is None: 46 | return { 47 | 'statusCode': 404, 48 | 'body': json.dumps({'message': 'Record Not Found'}) 49 | } 50 | 51 | article_content.pop('paid_body', None) 52 | article_info.update(article_content) 53 | 54 | return { 55 | 'statusCode': 200, 56 | 'body': json.dumps(article_info, cls=DecimalEncoder) 57 | } 58 | -------------------------------------------------------------------------------- /src/handlers/articles/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from articles_show import ArticlesShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | articles_show = ArticlesShow(event, context, dynamodb) 10 | return articles_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/articles/supporters/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from articles_supporters_index import ArticlesSupportersIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | articles_supporters_index = ArticlesSupportersIndex(event, context, dynamodb) 10 | return articles_supporters_index.main() 11 | -------------------------------------------------------------------------------- /src/handlers/articles/tip_ranking/articles_tip_ranking.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import settings 4 | from db_util import DBUtil 5 | from es_util import ESUtil 6 | from lambda_base import LambdaBase 7 | from jsonschema import validate 8 | from decimal_encoder import DecimalEncoder 9 | from parameter_util import ParameterUtil 10 | 11 | 12 | class ArticlesTipRanking(LambdaBase): 13 | def get_schema(self): 14 | return { 15 | 'type': 'object', 16 | 'properties': { 17 | 'limit': settings.parameters['limit'], 18 | 'page': settings.parameters['page'], 19 | 'topic': settings.parameters['topic'] 20 | } 21 | } 22 | 23 | def validate_params(self): 24 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 25 | 26 | validate(self.params, self.get_schema()) 27 | 28 | if self.params.get('topic'): 29 | DBUtil.validate_topic(self.dynamodb, self.params['topic']) 30 | 31 | def exec_main_proc(self): 32 | limit = int(self.params['limit']) if self.params.get('limit') else settings.ARTICLES_TIP_RAKING_DEFAULT_LIMIT 33 | page = int(self.params['page']) if self.params.get('page') else 1 34 | 35 | articles = ESUtil.search_tip_ranked_articles(self.elasticsearch, self.dynamodb, self.params, limit, page) 36 | 37 | response = { 38 | 'Items': articles 39 | } 40 | 41 | return { 42 | 'statusCode': 200, 43 | 'body': json.dumps(response, cls=DecimalEncoder) 44 | } 45 | -------------------------------------------------------------------------------- /src/handlers/articles/tip_ranking/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | import boto3 5 | from elasticsearch import Elasticsearch, RequestsHttpConnection 6 | from requests_aws4auth import AWS4Auth 7 | 8 | from articles_tip_ranking import ArticlesTipRanking 9 | 10 | dynamodb = boto3.resource('dynamodb') 11 | awsauth = AWS4Auth( 12 | os.environ['AWS_ACCESS_KEY_ID'], 13 | os.environ['AWS_SECRET_ACCESS_KEY'], 14 | os.environ['AWS_REGION'], 15 | 'es', 16 | session_token=os.environ['AWS_SESSION_TOKEN'] 17 | ) 18 | elasticsearch = Elasticsearch( 19 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 20 | http_auth=awsauth, 21 | use_ssl=True, 22 | verify_certs=True, 23 | connection_class=RequestsHttpConnection 24 | ) 25 | 26 | 27 | def lambda_handler(event, context): 28 | articles_tip_ranking = ArticlesTipRanking(event, context, dynamodb=dynamodb, elasticsearch=elasticsearch) 29 | return articles_tip_ranking.main() 30 | -------------------------------------------------------------------------------- /src/handlers/authorizer/handler.py: -------------------------------------------------------------------------------- 1 | from authorizer import Authorizer 2 | 3 | 4 | def lambda_handler(event, context): 5 | authorizer = Authorizer(event=event, context=context) 6 | return authorizer.main() 7 | -------------------------------------------------------------------------------- /src/handlers/cognito_trigger/custommessage/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from custom_message import CustomMessage 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | custommessage = CustomMessage(event=event, context=context, dynamodb=dynamodb) 10 | return custommessage.main() 11 | -------------------------------------------------------------------------------- /src/handlers/cognito_trigger/postconfirmation/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from post_confirmation import PostConfirmation 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | postconfirmation = PostConfirmation(event=event, context=context, dynamodb=dynamodb) 10 | postconfirmation.main() 11 | return event 12 | -------------------------------------------------------------------------------- /src/handlers/cognito_trigger/postconfirmation/post_confirmation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from cognito_trigger_base import CognitoTriggerBase 4 | 5 | 6 | # Todo: LambdaBase → CognitoTriggerBase への変更 7 | class PostConfirmation(CognitoTriggerBase): 8 | def get_schema(self): 9 | pass 10 | 11 | def validate_params(self): 12 | pass 13 | 14 | def exec_main_proc(self): 15 | # パスワード忘れの場合は処理対象外 16 | if self.event.get('triggerSource') == 'PostConfirmation_ConfirmForgotPassword': 17 | return True 18 | 19 | users = self.dynamodb.Table(os.environ['USERS_TABLE_NAME']) 20 | user = { 21 | 'user_id': self.event['userName'], 22 | 'user_display_name': self.event['userName'], 23 | 'sync_elasticsearch': 1 24 | } 25 | users.put_item(Item=user, ConditionExpression='attribute_not_exists(user_id)') 26 | if os.environ['BETA_MODE_FLAG'] == "1": 27 | beta_users = self.dynamodb.Table(os.environ['BETA_USERS_TABLE_NAME']) 28 | beta_user = { 29 | 'email': self.event['request']['userAttributes']['email'], 30 | 'used': True 31 | } 32 | beta_users.put_item(Item=beta_user) 33 | return True 34 | -------------------------------------------------------------------------------- /src/handlers/cognito_trigger/preauthentication/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from pre_authentication import PreAuthentication 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | preauthentication = PreAuthentication(event=event, context=context, dynamodb=dynamodb) 10 | return preauthentication.main() 11 | -------------------------------------------------------------------------------- /src/handlers/cognito_trigger/preauthentication/pre_authentication.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | import logging 5 | from lambda_base import LambdaBase 6 | from jsonschema import ValidationError 7 | from boto3.dynamodb.conditions import Key 8 | from botocore.exceptions import ClientError 9 | 10 | 11 | # Todo: LambdaBase → CognitoTriggerBase への変更 12 | class PreAuthentication(LambdaBase): 13 | def get_schema(self): 14 | pass 15 | 16 | def validate_params(self): 17 | pass 18 | 19 | def exec_main_proc(self): 20 | params = self.event 21 | external_provider_users_table = self.dynamodb.Table(os.environ['EXTERNAL_PROVIDER_USERS_TABLE_NAME']) 22 | external_provider_user = external_provider_users_table.get_item(Key={ 23 | 'external_provider_user_id': params['userName'] 24 | }).get('Item') 25 | if external_provider_user is None: 26 | try: 27 | external_provider_users = external_provider_users_table.query( 28 | IndexName="user_id-index", 29 | KeyConditionExpression=Key('user_id').eq(params['userName']) 30 | ) 31 | if external_provider_users.get('Count') == 1: 32 | external_provider_user = external_provider_users.get('Items')[0] 33 | except ClientError as e: 34 | logging.fatal(e) 35 | return { 36 | 'statusCode': 500, 37 | 'body': json.dumps({'message': 'Internal server error'}) 38 | } 39 | # 通常SignInのケース 40 | if external_provider_user is None: 41 | return self.event 42 | # ExternalProviderLoginのケース 43 | elif self.__is_external_provider_login_validation_data(params): 44 | return self.event 45 | else: 46 | raise ValidationError('Please login with registered external provider') 47 | 48 | @staticmethod 49 | def __is_external_provider_login_validation_data(params): 50 | if (('EXTERNAL_PROVIDER_LOGIN_MARK' in params['request']['validationData']) and 51 | (os.environ['EXTERNAL_PROVIDER_LOGIN_MARK'] == params['request']['validationData']['EXTERNAL_PROVIDER_LOGIN_MARK'])): 52 | return True 53 | return False 54 | -------------------------------------------------------------------------------- /src/handlers/cognito_trigger/presignup/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from pre_signup import PreSignUp 4 | 5 | cognito = boto3.client('cognito-idp') 6 | dynamodb = boto3.resource('dynamodb') 7 | 8 | 9 | def lambda_handler(event, context): 10 | presignup = PreSignUp(event=event, context=context, dynamodb=dynamodb, cognito=cognito) 11 | return presignup.main() 12 | -------------------------------------------------------------------------------- /src/handlers/comments/likes/show/comments_likes_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | import settings 5 | 6 | from boto3.dynamodb.conditions import Key 7 | from decimal_encoder import DecimalEncoder 8 | from lambda_base import LambdaBase 9 | from jsonschema import validate 10 | 11 | 12 | class CommentsLikesShow(LambdaBase): 13 | def get_schema(self): 14 | return { 15 | 'type': 'object', 16 | 'properties': { 17 | 'comment_id': settings.parameters['comment']['comment_id'] 18 | }, 19 | 'required': ['comment_id'] 20 | } 21 | 22 | def validate_params(self): 23 | validate(self.params, self.get_schema()) 24 | 25 | def exec_main_proc(self): 26 | comment_liked_user_table = self.dynamodb.Table(os.environ['COMMENT_LIKED_USER_TABLE_NAME']) 27 | 28 | query_params = { 29 | 'KeyConditionExpression': Key('comment_id').eq(self.event['pathParameters']['comment_id']), 30 | 'Select': 'COUNT' 31 | } 32 | 33 | response = comment_liked_user_table.query(**query_params) 34 | 35 | return { 36 | 'statusCode': 200, 37 | 'body': json.dumps({'count': response['Count']}, cls=DecimalEncoder) 38 | } 39 | -------------------------------------------------------------------------------- /src/handlers/comments/likes/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from comments_likes_show import CommentsLikesShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | comments_likes_show = CommentsLikesShow(event=event, context=context, dynamodb=dynamodb) 10 | return comments_likes_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/labo/n/license_token/file_download_url/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from license_token_file_download_url import LicenseTokenFileDownloadUrl 3 | 4 | 5 | def lambda_handler(event, context): 6 | target = LicenseTokenFileDownloadUrl(event, context) 7 | return target.main() 8 | -------------------------------------------------------------------------------- /src/handlers/labo/n/license_token/file_upload_url/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from license_token_file_upload_url import LicenseTokenFileUploadUrl 3 | 4 | 5 | def lambda_handler(event, context): 6 | target = LicenseTokenFileUploadUrl(event, context) 7 | return target.main() 8 | -------------------------------------------------------------------------------- /src/handlers/labo/n/majority_judgement/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from majority_judgement_create import LaboNMajorityJudgementCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | target = LaboNMajorityJudgementCreate(event, context, dynamodb) 10 | return target.main() 11 | -------------------------------------------------------------------------------- /src/handlers/labo/n/majority_judgement/create/majority_judgement_create.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import time 4 | from lambda_base import LambdaBase 5 | from jsonschema import validate 6 | 7 | options_count = 4 # 選択肢の数 8 | valuation_level = 7 # n段階評価の、n 9 | 10 | 11 | class LaboNMajorityJudgementCreate(LambdaBase): 12 | def get_schema(self): 13 | opt = { 14 | "type": "number", 15 | "minimum": 1, 16 | "maximum": valuation_level 17 | } 18 | 19 | required = [] 20 | properties = {} 21 | for i in range(options_count): 22 | key = 'opt_' + str(i + 1) 23 | properties[key] = opt 24 | required.append(key) 25 | 26 | for i in range(options_count): 27 | properties['opt_' + str(i + 1)] = opt 28 | 29 | return { 30 | "type": "object", 31 | "properties": properties, 32 | "required": required 33 | } 34 | 35 | def validate_params(self): 36 | validate(self.params, self.get_schema()) 37 | 38 | def exec_main_proc(self): 39 | table = self.dynamodb.Table(os.environ['MAJORITY_JUDGEMENT_TABLE_NAME']) 40 | 41 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 42 | if not LaboNMajorityJudgementCreate.__is_exists(table, user_id): 43 | item = { 44 | 'user_id': user_id, 45 | 'created_at': int(time.time()) 46 | } 47 | 48 | for key, value in self.params.items(): 49 | item[key] = value 50 | 51 | table.put_item( 52 | Item=item, 53 | ConditionExpression='attribute_not_exists(user_id)' 54 | ) 55 | 56 | return { 57 | 'statusCode': 200 58 | } 59 | 60 | @staticmethod 61 | def __is_exists(table, user_id): 62 | result = table.get_item(Key={'user_id': user_id}).get('Item') 63 | 64 | return False if result is None else True 65 | -------------------------------------------------------------------------------- /src/handlers/labo/n/majority_judgement/delete_all/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from majority_judgement_delete_all import LaboNMajorityJudgementDeleteAll 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | target = LaboNMajorityJudgementDeleteAll(event, context, dynamodb) 10 | return target.main() 11 | -------------------------------------------------------------------------------- /src/handlers/labo/n/majority_judgement/delete_all/majority_judgement_delete_all.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from lambda_base import LambdaBase 4 | 5 | 6 | class LaboNMajorityJudgementDeleteAll(LambdaBase): 7 | def get_schema(self): 8 | pass 9 | 10 | def validate_params(self): 11 | pass 12 | 13 | def exec_main_proc(self): 14 | table = self.dynamodb.Table(os.environ['MAJORITY_JUDGEMENT_TABLE_NAME']) 15 | 16 | LaboNMajorityJudgementDeleteAll.truncate_dynamo_items(table) 17 | 18 | return True 19 | 20 | @staticmethod 21 | def truncate_dynamo_items(dynamodb_table): 22 | delete_items = [] 23 | parameters = {} 24 | while True: 25 | response = dynamodb_table.scan(**parameters) 26 | delete_items.extend(response["Items"]) 27 | if ("LastEvaluatedKey" in response): 28 | parameters["ExclusiveStartKey"] = response["LastEvaluatedKey"] 29 | else: 30 | break 31 | 32 | key_names = [x["AttributeName"] for x in dynamodb_table.key_schema] 33 | delete_keys = [{k: v for k, v in x.items() if k in key_names} for x in delete_items] 34 | 35 | with dynamodb_table.batch_writer() as batch: 36 | for key in delete_keys: 37 | batch.delete_item(Key=key) 38 | 39 | return 0 40 | -------------------------------------------------------------------------------- /src/handlers/labo/n/majority_judgement/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from majority_judgement_index import LaboNMajorityJudgementIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | target = LaboNMajorityJudgementIndex(event, context, dynamodb) 10 | return target.main() 11 | -------------------------------------------------------------------------------- /src/handlers/labo/n/majority_judgement/index/majority_judgement_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | from lambda_base import LambdaBase 5 | 6 | 7 | class LaboNMajorityJudgementIndex(LambdaBase): 8 | def get_schema(self): 9 | pass 10 | 11 | def validate_params(self): 12 | pass 13 | 14 | def exec_main_proc(self): 15 | table = self.dynamodb.Table(os.environ['MAJORITY_JUDGEMENT_TABLE_NAME']) 16 | 17 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 18 | exists = LaboNMajorityJudgementIndex.__is_exists(table, user_id) 19 | 20 | return { 21 | 'statusCode': 200, 22 | 'body': json.dumps({'exists': exists}) 23 | } 24 | 25 | @staticmethod 26 | def __is_exists(table, user_id): 27 | result = table.get_item(Key={'user_id': user_id}).get('Item') 28 | 29 | return False if result is None else True 30 | -------------------------------------------------------------------------------- /src/handlers/labo/n/quadratic_voting/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from quadratic_voting_create import LaboNQuadraticVotingCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | target = LaboNQuadraticVotingCreate(event, context, dynamodb) 10 | return target.main() 11 | -------------------------------------------------------------------------------- /src/handlers/labo/n/quadratic_voting/create/quadratic_voting_create.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import time 4 | import math 5 | from lambda_base import LambdaBase 6 | from jsonschema import validate, ValidationError 7 | 8 | options_count = 6 # 選択肢の数 9 | credit_per_user = 100 # ユーザに付与されるクレジット(持ち越しは考慮しない) 10 | maximum = math.sqrt(credit_per_user) # 許容される最大の値 11 | 12 | 13 | class LaboNQuadraticVotingCreate(LambdaBase): 14 | def get_schema(self): 15 | opt = { 16 | "type": "integer", 17 | "minimum": 0, 18 | "maximum": maximum 19 | } 20 | 21 | required = [] 22 | properties = {} 23 | for i in range(options_count): 24 | key = 'opt_' + str(i + 1) 25 | properties[key] = opt 26 | required.append(key) 27 | 28 | for i in range(options_count): 29 | properties['opt_' + str(i + 1)] = opt 30 | 31 | return { 32 | "type": "object", 33 | "properties": properties, 34 | "required": required 35 | } 36 | 37 | def validate_params(self): 38 | validate(self.params, self.get_schema()) 39 | 40 | totalVotedValue = 0 41 | for key in self.params: 42 | totalVotedValue += self.params[key] ** 2 43 | 44 | # 投票の合計がCreditの限界を超えている場合 45 | if totalVotedValue > credit_per_user: 46 | raise ValidationError('Invalid') 47 | 48 | def exec_main_proc(self): 49 | table = self.dynamodb.Table(os.environ['QUADRATIC_VOTING_TABLE_NAME']) 50 | 51 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 52 | if not LaboNQuadraticVotingCreate.__is_exists(table, user_id): 53 | item = { 54 | 'user_id': user_id, 55 | 'created_at': int(time.time()) 56 | } 57 | 58 | for key, value in self.params.items(): 59 | item[key] = value 60 | 61 | table.put_item( 62 | Item=item, 63 | ConditionExpression='attribute_not_exists(user_id)' 64 | ) 65 | 66 | return { 67 | 'statusCode': 200 68 | } 69 | 70 | @staticmethod 71 | def __is_exists(table, user_id): 72 | result = table.get_item(Key={'user_id': user_id}).get('Item') 73 | 74 | return False if result is None else True 75 | -------------------------------------------------------------------------------- /src/handlers/labo/n/quadratic_voting/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from quadratic_voting_index import LaboNQuadraticVotingIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | target = LaboNQuadraticVotingIndex(event, context, dynamodb) 10 | return target.main() 11 | -------------------------------------------------------------------------------- /src/handlers/labo/n/quadratic_voting/index/quadratic_voting_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | from lambda_base import LambdaBase 5 | 6 | 7 | class LaboNQuadraticVotingIndex(LambdaBase): 8 | def get_schema(self): 9 | pass 10 | 11 | def validate_params(self): 12 | pass 13 | 14 | def exec_main_proc(self): 15 | table = self.dynamodb.Table(os.environ['QUADRATIC_VOTING_TABLE_NAME']) 16 | 17 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 18 | exists = LaboNQuadraticVotingIndex.__is_exists(table, user_id) 19 | 20 | return { 21 | 'statusCode': 200, 22 | 'body': json.dumps({'exists': exists}) 23 | } 24 | 25 | @staticmethod 26 | def __is_exists(table, user_id): 27 | result = table.get_item(Key={'user_id': user_id}).get('Item') 28 | 29 | return False if result is None else True 30 | -------------------------------------------------------------------------------- /src/handlers/labo/n/random/article.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | from es_util import ESUtil 5 | from lambda_base import LambdaBase 6 | from decimal_encoder import DecimalEncoder 7 | 8 | 9 | class LaboNRandomArticle(LambdaBase): 10 | def get_schema(self): 11 | pass 12 | 13 | def validate_params(self): 14 | pass 15 | 16 | def exec_main_proc(self): 17 | article_id = self.__get_random_article() 18 | 19 | article_info_table = self.dynamodb.Table(os.environ['ARTICLE_INFO_TABLE_NAME']) 20 | article_content_table = self.dynamodb.Table(os.environ['ARTICLE_CONTENT_TABLE_NAME']) 21 | 22 | article_info = article_info_table.get_item(Key={'article_id': article_id}).get('Item') 23 | article_content = article_content_table.get_item(Key={'article_id': article_id}).get('Item') 24 | 25 | if article_info is None or article_content is None: 26 | return { 27 | 'statusCode': 404, 28 | 'body': json.dumps({'message': 'Record Not Found'}) 29 | } 30 | 31 | article_content.pop('paid_body', None) 32 | article_info.update(article_content) 33 | 34 | return { 35 | 'statusCode': 200, 36 | 'body': json.dumps(article_info, cls=DecimalEncoder) 37 | } 38 | 39 | def __get_random_article(self): 40 | response = ESUtil.search_random_article(self.elasticsearch) 41 | 42 | article_id = response["hits"]["hits"][0]['_id'] 43 | 44 | return article_id 45 | -------------------------------------------------------------------------------- /src/handlers/labo/n/random/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import boto3 4 | from article import LaboNRandomArticle 5 | from elasticsearch import Elasticsearch, RequestsHttpConnection 6 | from requests_aws4auth import AWS4Auth 7 | 8 | dynamodb = boto3.resource('dynamodb') 9 | awsauth = AWS4Auth( 10 | os.environ['AWS_ACCESS_KEY_ID'], 11 | os.environ['AWS_SECRET_ACCESS_KEY'], 12 | os.environ['AWS_REGION'], 13 | 'es', 14 | session_token=os.environ['AWS_SESSION_TOKEN'] 15 | ) 16 | elasticsearch = Elasticsearch( 17 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 18 | http_auth=awsauth, 19 | use_ssl=True, 20 | verify_certs=True, 21 | connection_class=RequestsHttpConnection 22 | ) 23 | 24 | 25 | def lambda_handler(event, context): 26 | articles_random = LaboNRandomArticle(event, context, dynamodb, elasticsearch=elasticsearch) 27 | return articles_random.main() 28 | -------------------------------------------------------------------------------- /src/handlers/login/facebook/authorization_url/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from login_facebook_authorization_url import LoginFacebookAuthorizationUrl 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | login_facebook_authorization_url = LoginFacebookAuthorizationUrl( 10 | dynamodb=dynamodb, 11 | event=event, 12 | context=context 13 | ) 14 | return login_facebook_authorization_url.main() 15 | -------------------------------------------------------------------------------- /src/handlers/login/facebook/authorization_url/login_facebook_authorization_url.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import traceback 4 | from lambda_base import LambdaBase 5 | from facebook_util import FacebookUtil 6 | from botocore.exceptions import ClientError 7 | from response_builder import ResponseBuilder 8 | 9 | 10 | class LoginFacebookAuthorizationUrl(LambdaBase): 11 | def get_schema(self): 12 | pass 13 | 14 | def validate_params(self): 15 | pass 16 | 17 | def exec_main_proc(self): 18 | facebook = FacebookUtil( 19 | app_id=os.environ['FACEBOOK_APP_ID'], 20 | app_secret=os.environ['FACEBOOK_APP_SECRET'], 21 | callback_url=os.environ['FACEBOOK_OAUTH_CALLBACK_URL'], 22 | app_token=os.environ['FACEBOOK_APP_TOKEN'] 23 | ) 24 | try: 25 | authentication_url = facebook.get_authorization_url( 26 | dynamodb=self.dynamodb 27 | ) 28 | except (ClientError) as e: 29 | logging.info(self.event) 30 | logging.fatal(e) 31 | traceback.print_exc() 32 | return ResponseBuilder.response( 33 | status_code=500, 34 | body={'message': 'Internal server error'} 35 | ) 36 | return ResponseBuilder.response( 37 | status_code=200, 38 | body={'url': authentication_url} 39 | ) 40 | -------------------------------------------------------------------------------- /src/handlers/login/facebook/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from login_facebook_index import LoginFacebookIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | cognito = boto3.client('cognito-idp') 7 | 8 | 9 | def lambda_handler(event, context): 10 | login_facebook_index = LoginFacebookIndex( 11 | event=event, 12 | context=context, 13 | cognito=cognito, 14 | dynamodb=dynamodb 15 | ) 16 | return login_facebook_index.main() 17 | -------------------------------------------------------------------------------- /src/handlers/login/line/authorize_request/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from login_line_authorize_request import LoginLineAuthorizeRequest 3 | 4 | cognito = boto3.client('cognito-idp') 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | login_line_authorize_request = LoginLineAuthorizeRequest(event=event, context=context, dynamodb=dynamodb, cognito=cognito) 10 | return login_line_authorize_request.main() 11 | -------------------------------------------------------------------------------- /src/handlers/login/line/authorize_url/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from login_line_authorize_url import LoginLineAuthorizeUrl 3 | 4 | cognito = boto3.client('cognito-idp') 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | login_line_authorize_url = LoginLineAuthorizeUrl(event=event, context=context, dynamodb=dynamodb, cognito=cognito) 10 | return login_line_authorize_url.main() 11 | -------------------------------------------------------------------------------- /src/handlers/login/line/authorize_url/login_line_authorize_url.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import secrets 4 | import settings 5 | import json 6 | from lambda_base import LambdaBase 7 | 8 | 9 | class LoginLineAuthorizeUrl(LambdaBase): 10 | def get_schema(self): 11 | pass 12 | 13 | def validate_params(self): 14 | pass 15 | 16 | def exec_main_proc(self): 17 | redirect_url = '&redirect_uri=' + os.environ['LINE_REDIRECT_URI'] 18 | state_and_scope = '&state=' + self.__generate_state() + settings.LINE_LOGIN_REQUEST_SCOPE 19 | url = settings.LINE_AUTHORIZE_URL + os.environ['LINE_CHANNEL_ID'] + redirect_url + state_and_scope 20 | 21 | return { 22 | 'statusCode': 200, 23 | 'body': json.dumps({ 24 | 'callback_url': url 25 | }) 26 | } 27 | 28 | @staticmethod 29 | def __generate_state(): 30 | return secrets.token_hex(4) 31 | -------------------------------------------------------------------------------- /src/handlers/login/twitter/authorization_url/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from login_twitter_authorization_url import LoginTwitterAuthorizationUrl 3 | 4 | 5 | def lambda_handler(event, context): 6 | login_twitter_authorization_url = LoginTwitterAuthorizationUrl(event=event, context=context) 7 | return login_twitter_authorization_url.main() 8 | -------------------------------------------------------------------------------- /src/handlers/login/twitter/authorization_url/login_twitter_authorization_url.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import traceback 4 | from lambda_base import LambdaBase 5 | from twitter_util import TwitterUtil 6 | from exceptions import TwitterOauthError 7 | from response_builder import ResponseBuilder 8 | 9 | 10 | class LoginTwitterAuthorizationUrl(LambdaBase): 11 | def get_schema(self): 12 | pass 13 | 14 | def validate_params(self): 15 | pass 16 | 17 | def exec_main_proc(self): 18 | twitter = TwitterUtil( 19 | consumer_key=os.environ['TWITTER_CONSUMER_KEY'], 20 | consumer_secret=os.environ['TWITTER_CONSUMER_SECRET'] 21 | ) 22 | try: 23 | authentication_url = twitter.generate_auth_url( 24 | callback_url=os.environ['TWITTER_OAUTH_CALLBACK_URL'] 25 | ) 26 | except TwitterOauthError as e: 27 | logging.info(self.event) 28 | logging.fatal(e) 29 | traceback.print_exc() 30 | return ResponseBuilder.response( 31 | status_code=500, 32 | body={'message': 'Internal server error'} 33 | ) 34 | return ResponseBuilder.response( 35 | status_code=200, 36 | body={'url': authentication_url} 37 | ) 38 | -------------------------------------------------------------------------------- /src/handlers/login/twitter/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from login_twitter_index import LoginTwitterIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | cognito = boto3.client('cognito-idp') 7 | 8 | 9 | def lambda_handler(event, context): 10 | login_twitter_index = LoginTwitterIndex( 11 | event=event, 12 | context=context, 13 | dynamodb=dynamodb, 14 | cognito=cognito 15 | ) 16 | return login_twitter_index.main() 17 | -------------------------------------------------------------------------------- /src/handlers/login/yahoo/authorization_url/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from login_yahoo_authorization_url import LoginYahooAuthorizationUrl 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | login_yahoo_authorization_url = LoginYahooAuthorizationUrl( 10 | dynamodb=dynamodb, 11 | event=event, 12 | context=context 13 | ) 14 | return login_yahoo_authorization_url.main() 15 | -------------------------------------------------------------------------------- /src/handlers/login/yahoo/authorization_url/login_yahoo_authorization_url.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import traceback 4 | from lambda_base import LambdaBase 5 | from yahoo_util import YahooUtil 6 | from exceptions import YahooOauthError 7 | from botocore.exceptions import ClientError 8 | from response_builder import ResponseBuilder 9 | 10 | 11 | class LoginYahooAuthorizationUrl(LambdaBase): 12 | def get_schema(self): 13 | pass 14 | 15 | def validate_params(self): 16 | pass 17 | 18 | def exec_main_proc(self): 19 | yahoo = YahooUtil( 20 | client_id=os.environ['YAHOO_CLIENT_ID'], 21 | secret=os.environ['YAHOO_SECRET'], 22 | callback_url=os.environ['YAHOO_OAUTH_CALLBACK_URL'] 23 | ) 24 | try: 25 | authentication_url = yahoo.get_authorization_url( 26 | dynamodb=self.dynamodb 27 | ) 28 | except (ClientError, YahooOauthError) as e: 29 | logging.info(self.event) 30 | logging.fatal(e) 31 | traceback.print_exc() 32 | return ResponseBuilder.response( 33 | status_code=500, 34 | body={'message': 'Internal server error'} 35 | ) 36 | return ResponseBuilder.response( 37 | status_code=200, 38 | body={'url': authentication_url} 39 | ) 40 | -------------------------------------------------------------------------------- /src/handlers/login/yahoo/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from login_yahoo_index import LoginYahooIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | cognito = boto3.client('cognito-idp') 7 | 8 | 9 | def lambda_handler(event, context): 10 | login_yahoo_index = LoginYahooIndex( 11 | event=event, 12 | context=context, 13 | cognito=cognito, 14 | dynamodb=dynamodb 15 | ) 16 | return login_yahoo_index.main() 17 | -------------------------------------------------------------------------------- /src/handlers/me/allowed_applications/delete/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from me_allowed_applications_delete import MeAllowedApplicationsDelete 3 | 4 | 5 | def lambda_handler(event, context): 6 | me_allowed_applications_delete = MeAllowedApplicationsDelete(event=event, context=context) 7 | return me_allowed_applications_delete.main() 8 | -------------------------------------------------------------------------------- /src/handlers/me/allowed_applications/delete/me_allowed_applications_delete.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | from jsonschema import validate 4 | 5 | import settings 6 | from authlete_util import AuthleteUtil 7 | from lambda_base import LambdaBase 8 | 9 | 10 | class MeAllowedApplicationsDelete(LambdaBase): 11 | def get_schema(self): 12 | return { 13 | 'type': 'object', 14 | 'properties': { 15 | 'client_id': settings.parameters['oauth_client']['client_id'] 16 | }, 17 | 'required': ['client_id'] 18 | } 19 | 20 | def validate_params(self): 21 | validate(self.params, self.get_schema()) 22 | 23 | def exec_main_proc(self): 24 | subject = self.event['requestContext']['authorizer']['claims']['cognito:username'] 25 | url = settings.AUTHLETE_CLIENT_ENDPOINT + '/authorization/delete/' + str(self.params['client_id']) + '/' + subject 26 | try: 27 | response = requests.delete( 28 | url, 29 | auth=(os.environ['AUTHLETE_API_KEY'], os.environ['AUTHLETE_API_SECRET']) 30 | ) 31 | except requests.exceptions.RequestException as err: 32 | raise Exception('Something went wrong when call Authlete API: {0}'.format(err)) 33 | 34 | AuthleteUtil.verify_valid_response(response, request_client_id=self.params['client_id']) 35 | 36 | return { 37 | 'statusCode': 200, 38 | 'body': '{"result": "OK"}' 39 | } 40 | -------------------------------------------------------------------------------- /src/handlers/me/allowed_applications/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from me_allowed_applications_index import MeAllowedApplicationsIndex 3 | 4 | 5 | def lambda_handler(event, context): 6 | me_allowed_applications_index = MeAllowedApplicationsIndex(event=event, context=context) 7 | return me_allowed_applications_index.main() 8 | -------------------------------------------------------------------------------- /src/handlers/me/applications/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from me_applications_create import MeApplicationsCreate 3 | 4 | 5 | def lambda_handler(event, context): 6 | me_applications_create = MeApplicationsCreate(event=event, context=context) 7 | return me_applications_create.main() 8 | -------------------------------------------------------------------------------- /src/handlers/me/applications/delete/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from me_applications_delete import MeApplicationDelete 3 | 4 | 5 | def lambda_handler(event, context): 6 | me_applications_delete = MeApplicationDelete(event=event, context=context) 7 | return me_applications_delete.main() 8 | -------------------------------------------------------------------------------- /src/handlers/me/applications/delete/me_applications_delete.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | from jsonschema import validate 4 | 5 | import settings 6 | from authlete_util import AuthleteUtil 7 | from lambda_base import LambdaBase 8 | from no_permission_error import NoPermissionError 9 | from parameter_util import ParameterUtil 10 | 11 | 12 | class MeApplicationDelete(LambdaBase): 13 | def get_schema(self): 14 | return { 15 | 'type': 'object', 16 | 'properties': { 17 | 'client_id': settings.parameters['oauth_client']['client_id'] 18 | }, 19 | 'required': ['client_id'] 20 | } 21 | 22 | def validate_params(self): 23 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 24 | validate(self.params, self.get_schema()) 25 | 26 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 27 | 28 | if not AuthleteUtil.is_accessible_client(self.params['client_id'], user_id): 29 | raise NoPermissionError('No permission on this resource') 30 | 31 | def exec_main_proc(self): 32 | try: 33 | response = requests.delete( 34 | settings.AUTHLETE_CLIENT_ENDPOINT + '/delete/' + str(self.params['client_id']), 35 | auth=(os.environ['AUTHLETE_API_KEY'], os.environ['AUTHLETE_API_SECRET']) 36 | ) 37 | except requests.exceptions.RequestException as err: 38 | raise Exception('Something went wrong when call Authlete API: {0}'.format(err)) 39 | 40 | AuthleteUtil.verify_valid_response(response, request_client_id=self.params['client_id']) 41 | 42 | return { 43 | 'statusCode': 200, 44 | 'body': response.text 45 | } 46 | -------------------------------------------------------------------------------- /src/handlers/me/applications/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from me_applications_index import MeApplicationIndex 3 | 4 | 5 | def lambda_handler(event, context): 6 | me_applications_index = MeApplicationIndex(event=event, context=context) 7 | return me_applications_index.main() 8 | -------------------------------------------------------------------------------- /src/handlers/me/applications/index/me_applications_index.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import requests 4 | 5 | import settings 6 | from authlete_util import AuthleteUtil 7 | from lambda_base import LambdaBase 8 | 9 | 10 | class MeApplicationIndex(LambdaBase): 11 | def get_schema(self): 12 | pass 13 | 14 | def validate_params(self): 15 | pass 16 | 17 | def exec_main_proc(self): 18 | index_params = {'developer': self.event['requestContext']['authorizer']['claims']['cognito:username']} 19 | 20 | try: 21 | response = requests.get( 22 | settings.AUTHLETE_CLIENT_ENDPOINT + '/get/list', 23 | params=index_params, 24 | auth=(os.environ['AUTHLETE_API_KEY'], os.environ['AUTHLETE_API_SECRET']) 25 | ) 26 | 27 | except requests.exceptions.RequestException as err: 28 | raise Exception('Something went wrong when call Authlete API: {0}'.format(err)) 29 | 30 | AuthleteUtil.verify_valid_response(response) 31 | 32 | return { 33 | 'statusCode': 200, 34 | 'body': response.text 35 | } 36 | -------------------------------------------------------------------------------- /src/handlers/me/applications/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from me_applications_show import MeApplicationShow 3 | 4 | 5 | def lambda_handler(event, context): 6 | me_applications_show = MeApplicationShow(event=event, context=context) 7 | return me_applications_show.main() 8 | -------------------------------------------------------------------------------- /src/handlers/me/applications/show/me_applications_show.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import requests 4 | from jsonschema import validate 5 | 6 | import settings 7 | from authlete_util import AuthleteUtil 8 | from lambda_base import LambdaBase 9 | from no_permission_error import NoPermissionError 10 | from parameter_util import ParameterUtil 11 | 12 | 13 | class MeApplicationShow(LambdaBase): 14 | def get_schema(self): 15 | return { 16 | 'type': 'object', 17 | 'properties': { 18 | 'client_id': settings.parameters['oauth_client']['client_id'] 19 | }, 20 | 'required': ['client_id'] 21 | } 22 | 23 | def validate_params(self): 24 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 25 | validate(self.params, self.get_schema()) 26 | 27 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 28 | 29 | if not AuthleteUtil.is_accessible_client(self.params['client_id'], user_id): 30 | raise NoPermissionError('No permission on this resource') 31 | 32 | def exec_main_proc(self): 33 | try: 34 | response = requests.get( 35 | settings.AUTHLETE_CLIENT_ENDPOINT + '/get/' + str(self.params['client_id']), 36 | auth=(os.environ['AUTHLETE_API_KEY'], os.environ['AUTHLETE_API_SECRET']) 37 | ) 38 | except requests.exceptions.RequestException as err: 39 | raise Exception('Something went wrong when call Authlete API: {0}'.format(err)) 40 | 41 | AuthleteUtil.verify_valid_response(response, request_client_id=self.params['client_id']) 42 | 43 | return { 44 | 'statusCode': 200, 45 | 'body': response.text 46 | } 47 | -------------------------------------------------------------------------------- /src/handlers/me/applications/update/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from me_applications_update import MeApplicationUpdate 3 | 4 | 5 | def lambda_handler(event, context): 6 | me_applications_update = MeApplicationUpdate(event=event, context=context) 7 | return me_applications_update.main() 8 | -------------------------------------------------------------------------------- /src/handlers/me/articles/comments/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_comments_create import MeArticlesCommentsCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_comments_create = MeArticlesCommentsCreate(event=event, context=context, dynamodb=dynamodb) 10 | return me_articles_comments_create.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/comments/likes/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_comments_likes_index import MeArticlesCommentsLikesIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_comments_likes_index = MeArticlesCommentsLikesIndex(event=event, context=context, dynamodb=dynamodb) 10 | return me_articles_comments_likes_index.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/comments/likes/index/me_articles_comments_likes_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | import settings 5 | 6 | from boto3.dynamodb.conditions import Key 7 | from db_util import DBUtil 8 | from lambda_base import LambdaBase 9 | from jsonschema import validate 10 | 11 | 12 | class MeArticlesCommentsLikesIndex(LambdaBase): 13 | def get_schema(self): 14 | return { 15 | 'type': 'object', 16 | 'properties': { 17 | 'article_id': settings.parameters['article_id'] 18 | }, 19 | 'required': ['article_id'] 20 | } 21 | 22 | def validate_params(self): 23 | validate(self.params, self.get_schema()) 24 | DBUtil.validate_article_existence(self.dynamodb, self.params['article_id'], status='public') 25 | 26 | def exec_main_proc(self): 27 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 28 | 29 | comment_liked_user_table = self.dynamodb.Table(os.environ['COMMENT_LIKED_USER_TABLE_NAME']) 30 | 31 | query_params = { 32 | 'IndexName': 'article_id-index', 33 | 'KeyConditionExpression': Key('article_id').eq(self.params['article_id']), 34 | } 35 | 36 | result = DBUtil.query_all_items(comment_liked_user_table, query_params) 37 | 38 | comment_ids = [liked_user['comment_id'] for liked_user in result if liked_user['user_id'] == user_id] 39 | 40 | return { 41 | 'statusCode': 200, 42 | 'body': json.dumps({'comment_ids': comment_ids}) 43 | } 44 | -------------------------------------------------------------------------------- /src/handlers/me/articles/comments/reply/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_comments_reply import MeArticlesCommentsReply 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_comments_reply = MeArticlesCommentsReply(event=event, context=context, dynamodb=dynamodb) 10 | return me_articles_comments_reply.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/content_edit_histories/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_content_edit_histories_index import MeArticlesContentEditHistoriesIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_content_edit_histories_index = MeArticlesContentEditHistoriesIndex(event, context, dynamodb) 10 | return me_articles_content_edit_histories_index.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/content_edit_histories/index/me_articles_content_edit_histories_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | import settings 5 | from boto3.dynamodb.conditions import Key 6 | from lambda_base import LambdaBase 7 | from jsonschema import validate 8 | from decimal_encoder import DecimalEncoder 9 | from user_util import UserUtil 10 | from db_util import DBUtil 11 | 12 | 13 | class MeArticlesContentEditHistoriesIndex(LambdaBase): 14 | def get_schema(self): 15 | return { 16 | 'type': 'object', 17 | 'properties': { 18 | 'article_id': settings.parameters['article_id'] 19 | }, 20 | 'required': ['article_id'] 21 | } 22 | 23 | def validate_params(self): 24 | UserUtil.verified_phone_and_email(self.event) 25 | validate(self.params, self.get_schema()) 26 | # 該当 article_id が自分のものかつ、v2であることを確認 27 | DBUtil.validate_article_existence( 28 | self.dynamodb, 29 | self.params['article_id'], 30 | user_id=self.event['requestContext']['authorizer']['claims']['cognito:username'], 31 | version=2 32 | ) 33 | 34 | def exec_main_proc(self): 35 | article_content_edit_history_table = self.dynamodb.Table(os.environ['ARTICLE_CONTENT_EDIT_HISTORY_TABLE_NAME']) 36 | 37 | # article_id-sort_key-index は射影に body を含めておらず 1MB を超えないため、LastEvaluatedKey の確認は不要 38 | query_params = { 39 | 'IndexName': 'article_id-sort_key-index', 40 | 'KeyConditionExpression': Key('article_id').eq(self.params['article_id']), 41 | 'ScanIndexForward': False 42 | } 43 | response = article_content_edit_history_table.query(**query_params) 44 | 45 | return { 46 | 'statusCode': 200, 47 | 'body': json.dumps(response, cls=DecimalEncoder) 48 | } 49 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/article_id/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_drafts_article_id_create import MeArticlesDraftsArticleIdCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_drafts_article_id_create = MeArticlesDraftsArticleIdCreate(event, context, dynamodb) 10 | return me_articles_drafts_article_id_create.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/body/update/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_drafts_body_update import MeArticlesDraftsBodyUpdate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_drafts_body_update = MeArticlesDraftsBodyUpdate(event, context, dynamodb) 10 | return me_articles_drafts_body_update.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/body/update/me_articles_drafts_body_update.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import settings 4 | from lambda_base import LambdaBase 5 | from jsonschema import validate, FormatChecker 6 | from db_util import DBUtil 7 | from user_util import UserUtil 8 | from text_sanitizer import TextSanitizer 9 | 10 | 11 | class MeArticlesDraftsBodyUpdate(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'article_id': settings.parameters['article_id'], 17 | 'body': settings.parameters['body'] 18 | }, 19 | 'required': ['article_id', 'body'] 20 | } 21 | 22 | def validate_params(self): 23 | UserUtil.verified_phone_and_email(self.event) 24 | validate(self.params, self.get_schema(), format_checker=FormatChecker()) 25 | DBUtil.validate_article_existence( 26 | self.dynamodb, 27 | self.params['article_id'], 28 | user_id=self.event['requestContext']['authorizer']['claims']['cognito:username'], 29 | status='draft', 30 | version=2 31 | ) 32 | 33 | def exec_main_proc(self): 34 | # 下書き記事を保存 35 | expression_attribute_values = { 36 | ':body': TextSanitizer.sanitize_article_body_v2(self.params.get('body')) 37 | } 38 | DBUtil.items_values_empty_to_none(expression_attribute_values) 39 | self.__update_article_content(expression_attribute_values) 40 | # 履歴を保存 41 | DBUtil.put_article_content_edit_history( 42 | dynamodb=self.dynamodb, 43 | user_id=self.event['requestContext']['authorizer']['claims']['cognito:username'], 44 | article_id=self.params.get('article_id'), 45 | sanitized_body=expression_attribute_values[':body'] 46 | ) 47 | 48 | return { 49 | 'statusCode': 200 50 | } 51 | 52 | def __update_article_content(self, expression_attribute_values): 53 | article_content_table = self.dynamodb.Table(os.environ['ARTICLE_CONTENT_TABLE_NAME']) 54 | 55 | article_content_table.update_item( 56 | Key={ 57 | 'article_id': self.params['article_id'], 58 | }, 59 | UpdateExpression="set body=:body", 60 | ExpressionAttributeValues=expression_attribute_values 61 | ) 62 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_drafts_create import MeArticlesDraftsCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_drafts_create = MeArticlesDraftsCreate(event, context, dynamodb) 10 | return me_articles_drafts_create.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/delete/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from me_articles_drafts_delete import MeArticlesDraftsDelete 3 | 4 | dynamodb = boto3.resource('dynamodb') 5 | 6 | 7 | def lambda_handler(event, context): 8 | me_articles_drafts_delete = MeArticlesDraftsDelete(event, context, dynamodb=dynamodb) 9 | return me_articles_drafts_delete.main() 10 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/delete/me_articles_drafts_delete.py: -------------------------------------------------------------------------------- 1 | import os 2 | import settings 3 | 4 | from lambda_base import LambdaBase 5 | from jsonschema import validate 6 | from db_util import DBUtil 7 | from user_util import UserUtil 8 | 9 | 10 | class MeArticlesDraftsDelete(LambdaBase): 11 | def get_schema(self): 12 | return { 13 | 'type': 'object', 14 | 'properties': { 15 | 'article_id': settings.parameters['article_id'] 16 | }, 17 | 'required': ['article_id'] 18 | } 19 | 20 | def validate_params(self): 21 | UserUtil.verified_phone_and_email(self.event) 22 | validate(self.params, self.get_schema()) 23 | 24 | DBUtil.validate_article_existence( 25 | self.dynamodb, 26 | self.params['article_id'], 27 | user_id=self.event['requestContext']['authorizer']['claims']['cognito:username'], 28 | status='draft' 29 | ) 30 | 31 | def exec_main_proc(self): 32 | article_info_table = self.dynamodb.Table(os.environ['ARTICLE_INFO_TABLE_NAME']) 33 | self.__update_article_info(article_info_table) 34 | 35 | return { 36 | 'statusCode': 200 37 | } 38 | 39 | def __update_article_info(self, article_info_table): 40 | info_expression_attribute_values = { 41 | ':article_status': 'delete', 42 | } 43 | 44 | info_update_expression = 'set #attr = :article_status' 45 | 46 | article_info_table.update_item( 47 | Key={ 48 | 'article_id': self.params['article_id'], 49 | }, 50 | UpdateExpression=info_update_expression, 51 | ExpressionAttributeNames={'#attr': 'status'}, 52 | ExpressionAttributeValues=info_expression_attribute_values 53 | ) 54 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_drafts_index import MeArticlesDraftsIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_drafts_index = MeArticlesDraftsIndex(event, context, dynamodb) 10 | return me_articles_drafts_index.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/publish/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | import boto3 5 | from elasticsearch import Elasticsearch, RequestsHttpConnection 6 | from requests_aws4auth import AWS4Auth 7 | 8 | from me_articles_drafts_publish import MeArticlesDraftsPublish 9 | 10 | dynamodb = boto3.resource('dynamodb') 11 | awsauth = AWS4Auth( 12 | os.environ['AWS_ACCESS_KEY_ID'], 13 | os.environ['AWS_SECRET_ACCESS_KEY'], 14 | os.environ['AWS_REGION'], 15 | 'es', 16 | session_token=os.environ['AWS_SESSION_TOKEN'] 17 | ) 18 | elasticsearch = Elasticsearch( 19 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 20 | http_auth=awsauth, 21 | use_ssl=True, 22 | verify_certs=True, 23 | connection_class=RequestsHttpConnection 24 | ) 25 | 26 | 27 | def lambda_handler(event, context): 28 | me_articles_drafts_publish = MeArticlesDraftsPublish(event, context, dynamodb=dynamodb, elasticsearch=elasticsearch) 29 | return me_articles_drafts_publish.main() 30 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/publish_with_header/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | import boto3 5 | from elasticsearch import Elasticsearch, RequestsHttpConnection 6 | from requests_aws4auth import AWS4Auth 7 | 8 | from me_articles_drafts_publish_with_header import MeArticlesDraftsPublishWithHeader 9 | 10 | dynamodb = boto3.resource('dynamodb') 11 | awsauth = AWS4Auth( 12 | os.environ['AWS_ACCESS_KEY_ID'], 13 | os.environ['AWS_SECRET_ACCESS_KEY'], 14 | os.environ['AWS_REGION'], 15 | 'es', 16 | session_token=os.environ['AWS_SESSION_TOKEN'] 17 | ) 18 | elasticsearch = Elasticsearch( 19 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 20 | http_auth=awsauth, 21 | use_ssl=True, 22 | verify_certs=True, 23 | connection_class=RequestsHttpConnection 24 | ) 25 | 26 | 27 | def lambda_handler(event, context): 28 | me_articles_drafts_publish_with_header = MeArticlesDraftsPublishWithHeader(event, context, dynamodb=dynamodb, 29 | elasticsearch=elasticsearch) 30 | return me_articles_drafts_publish_with_header.main() 31 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_drafts_show import MeArticlesDraftsShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_drafts_show = MeArticlesDraftsShow(event, context, dynamodb) 10 | return me_articles_drafts_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/title/update/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_drafts_title_update import MeArticlesDraftsTitleUpdate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_drafts_title_update = MeArticlesDraftsTitleUpdate(event, context, dynamodb) 10 | return me_articles_drafts_title_update.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/drafts/update/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_drafts_update import MeArticlesDraftsUpdate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_drafts_update = MeArticlesDraftsUpdate(event, context, dynamodb) 10 | return me_articles_drafts_update.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/fraud/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_fraud_create import MeArticlesFraudCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_fraud_create = MeArticlesFraudCreate(event=event, context=context, dynamodb=dynamodb) 10 | return me_articles_fraud_create.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/image_upload_url/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_image_upload_url_show import MeArticlesImageUploadUrlShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_image_upload_url_show = MeArticlesImageUploadUrlShow( 10 | event=event, context=context, dynamodb=dynamodb 11 | ) 12 | return me_articles_image_upload_url_show.main() 13 | -------------------------------------------------------------------------------- /src/handlers/me/articles/images/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_images_create import MeArticlesImagesCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | s3 = boto3.resource('s3') 7 | 8 | 9 | def lambda_handler(event, context): 10 | me_articles_images_create = MeArticlesImagesCreate(event=event, context=context, dynamodb=dynamodb, s3=s3) 11 | return me_articles_images_create.main() 12 | -------------------------------------------------------------------------------- /src/handlers/me/articles/like/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_like_create import MeArticlesLikeCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | articles_article_id_likes_post = MeArticlesLikeCreate(event=event, context=context, dynamodb=dynamodb) 10 | return articles_article_id_likes_post.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/like/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_like_show import MeArticleLikeShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_like_show = MeArticleLikeShow(event=event, context=context, dynamodb=dynamodb) 10 | return me_articles_like_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/like/show/me_articles_like_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import settings 4 | import json 5 | from db_util import DBUtil 6 | from lambda_base import LambdaBase 7 | from jsonschema import validate, ValidationError 8 | 9 | 10 | class MeArticleLikeShow(LambdaBase): 11 | def get_schema(self): 12 | return { 13 | 'type': 'object', 14 | 'properties': { 15 | 'article_id': settings.parameters['article_id'] 16 | }, 17 | 'required': ['article_id'] 18 | } 19 | 20 | def validate_params(self): 21 | # single 22 | if self.event.get('pathParameters') is None: 23 | raise ValidationError('pathParameters is required') 24 | validate(self.event.get('pathParameters'), self.get_schema()) 25 | # relation 26 | DBUtil.validate_article_existence( 27 | self.dynamodb, 28 | self.event['pathParameters']['article_id'], 29 | status='public' 30 | ) 31 | 32 | def exec_main_proc(self): 33 | article_liked_user_table = self.dynamodb.Table(os.environ['ARTICLE_LIKED_USER_TABLE_NAME']) 34 | 35 | responce = article_liked_user_table.get_item( 36 | Key={ 37 | 'article_id': self.event['pathParameters']['article_id'], 38 | 'user_id': self.event['requestContext']['authorizer']['claims']['cognito:username'] 39 | } 40 | ) 41 | 42 | liked = True if responce.get('Item') is not None else False 43 | 44 | return { 45 | 'statusCode': 200, 46 | 'body': json.dumps({'liked': liked}) 47 | } 48 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/body/update/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_public_body_update import MeArticlesPublicBodyUpdate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_public_body_update = MeArticlesPublicBodyUpdate(event, context, dynamodb) 10 | return me_articles_public_body_update.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/body/update/me_articles_public_body_update.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import settings 4 | from lambda_base import LambdaBase 5 | from jsonschema import validate, FormatChecker 6 | from db_util import DBUtil 7 | from user_util import UserUtil 8 | from text_sanitizer import TextSanitizer 9 | 10 | 11 | class MeArticlesPublicBodyUpdate(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'article_id': settings.parameters['article_id'], 17 | 'body': settings.parameters['body'] 18 | }, 19 | 'required': ['article_id', 'body'] 20 | } 21 | 22 | def validate_params(self): 23 | UserUtil.verified_phone_and_email(self.event) 24 | validate(self.params, self.get_schema(), format_checker=FormatChecker()) 25 | 26 | DBUtil.validate_article_existence( 27 | self.dynamodb, 28 | self.params['article_id'], 29 | user_id=self.event['requestContext']['authorizer']['claims']['cognito:username'], 30 | status='public', 31 | version=2 32 | ) 33 | 34 | def exec_main_proc(self): 35 | # 編集記事を保存 36 | article_content_edit_table = self.dynamodb.Table(os.environ['ARTICLE_CONTENT_EDIT_TABLE_NAME']) 37 | expression_attribute_values = { 38 | ':user_id': self.event['requestContext']['authorizer']['claims']['cognito:username'], 39 | ':body': TextSanitizer.sanitize_article_body_v2(self.params.get('body')) 40 | } 41 | DBUtil.items_values_empty_to_none(expression_attribute_values) 42 | article_content_edit_table.update_item( 43 | Key={ 44 | 'article_id': self.params['article_id'], 45 | }, 46 | UpdateExpression="set user_id=:user_id, body=:body", 47 | ExpressionAttributeValues=expression_attribute_values 48 | ) 49 | # 履歴を保存 50 | DBUtil.put_article_content_edit_history( 51 | dynamodb=self.dynamodb, 52 | user_id=expression_attribute_values[':user_id'], 53 | article_id=self.params.get('article_id'), 54 | sanitized_body=expression_attribute_values[':body'] 55 | ) 56 | 57 | return { 58 | 'statusCode': 200 59 | } 60 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/edit/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_public_edit import MeArticlesPublicEdit 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_public_edit = MeArticlesPublicEdit(event, context, dynamodb) 10 | return me_articles_public_edit.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_public_index import MeArticlesPublicIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_public_index = MeArticlesPublicIndex(event, context, dynamodb) 10 | return me_articles_public_index.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/republish/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | import boto3 5 | from elasticsearch import Elasticsearch, RequestsHttpConnection 6 | from requests_aws4auth import AWS4Auth 7 | 8 | from me_articles_public_republish import MeArticlesPublicRepublish 9 | 10 | dynamodb = boto3.resource('dynamodb') 11 | awsauth = AWS4Auth( 12 | os.environ['AWS_ACCESS_KEY_ID'], 13 | os.environ['AWS_SECRET_ACCESS_KEY'], 14 | os.environ['AWS_REGION'], 15 | 'es', 16 | session_token=os.environ['AWS_SESSION_TOKEN'] 17 | ) 18 | elasticsearch = Elasticsearch( 19 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 20 | http_auth=awsauth, 21 | use_ssl=True, 22 | verify_certs=True, 23 | connection_class=RequestsHttpConnection 24 | ) 25 | 26 | 27 | def lambda_handler(event, context): 28 | me_articles_public_republish = MeArticlesPublicRepublish(event, context, dynamodb=dynamodb, elasticsearch=elasticsearch) 29 | return me_articles_public_republish.main() 30 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/republish_with_header/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | import boto3 5 | from elasticsearch import Elasticsearch, RequestsHttpConnection 6 | from requests_aws4auth import AWS4Auth 7 | 8 | from me_articles_public_republish_with_header import MeArticlesPublicRepublishWithHeader 9 | 10 | dynamodb = boto3.resource('dynamodb') 11 | awsauth = AWS4Auth( 12 | os.environ['AWS_ACCESS_KEY_ID'], 13 | os.environ['AWS_SECRET_ACCESS_KEY'], 14 | os.environ['AWS_REGION'], 15 | 'es', 16 | session_token=os.environ['AWS_SESSION_TOKEN'] 17 | ) 18 | elasticsearch = Elasticsearch( 19 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 20 | http_auth=awsauth, 21 | use_ssl=True, 22 | verify_certs=True, 23 | connection_class=RequestsHttpConnection 24 | ) 25 | 26 | 27 | def lambda_handler(event, context): 28 | me_articles_public_republish_with_header = MeArticlesPublicRepublishWithHeader(event, context, dynamodb=dynamodb, 29 | elasticsearch=elasticsearch) 30 | return me_articles_public_republish_with_header.main() 31 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_public_show import MeArticlesPublicShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_public_show = MeArticlesPublicShow(event, context, dynamodb) 10 | return me_articles_public_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/show/me_articles_public_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | import settings 5 | from lambda_base import LambdaBase 6 | from jsonschema import validate, ValidationError 7 | from decimal_encoder import DecimalEncoder 8 | from db_util import DBUtil 9 | 10 | 11 | class MeArticlesPublicShow(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'article_id': settings.parameters['article_id'] 17 | }, 18 | 'required': ['article_id'] 19 | } 20 | 21 | def validate_params(self): 22 | if self.event.get('pathParameters') is None: 23 | raise ValidationError('pathParameters is required') 24 | 25 | validate(self.event.get('pathParameters'), self.get_schema()) 26 | 27 | DBUtil.validate_article_existence( 28 | self.dynamodb, 29 | self.params['article_id'], 30 | user_id=self.event['requestContext']['authorizer']['claims']['cognito:username'], 31 | status='public' 32 | ) 33 | 34 | def exec_main_proc(self): 35 | article_info_table = self.dynamodb.Table(os.environ['ARTICLE_INFO_TABLE_NAME']) 36 | article_content_table = self.dynamodb.Table(os.environ['ARTICLE_CONTENT_TABLE_NAME']) 37 | 38 | article_info = article_info_table.get_item(Key={'article_id': self.params['article_id']}).get('Item') 39 | article_content = article_content_table.get_item(Key={'article_id': self.params['article_id']}).get('Item') 40 | 41 | if 'price' in article_info: 42 | article_content['body'] = article_content['paid_body'] 43 | article_content.pop('paid_body', None) 44 | 45 | article_info.update(article_content) 46 | 47 | return { 48 | 'statusCode': 200, 49 | 'body': json.dumps(article_info, cls=DecimalEncoder) 50 | } 51 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/title/update/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_public_title_update import MeArticlesPublicTitleUpdate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_public_title_update = MeArticlesPublicTitleUpdate(event, context, dynamodb) 10 | return me_articles_public_title_update.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/title/update/me_articles_public_title_update.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import settings 4 | from lambda_base import LambdaBase 5 | from jsonschema import validate, FormatChecker 6 | from text_sanitizer import TextSanitizer 7 | from db_util import DBUtil 8 | from user_util import UserUtil 9 | 10 | 11 | class MeArticlesPublicTitleUpdate(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'article_id': settings.parameters['article_id'], 17 | 'title': settings.parameters['title'] 18 | }, 19 | 'required': ['article_id', 'title'] 20 | } 21 | 22 | def validate_params(self): 23 | UserUtil.verified_phone_and_email(self.event) 24 | validate(self.params, self.get_schema(), format_checker=FormatChecker()) 25 | 26 | DBUtil.validate_article_existence( 27 | self.dynamodb, 28 | self.params['article_id'], 29 | user_id=self.event['requestContext']['authorizer']['claims']['cognito:username'], 30 | status='public', 31 | version=2 32 | ) 33 | 34 | def exec_main_proc(self): 35 | article_content_edit_table = self.dynamodb.Table(os.environ['ARTICLE_CONTENT_EDIT_TABLE_NAME']) 36 | 37 | expression_attribute_values = { 38 | ':user_id': self.event['requestContext']['authorizer']['claims']['cognito:username'], 39 | ':title': TextSanitizer.sanitize_text(self.params.get('title')) 40 | } 41 | DBUtil.items_values_empty_to_none(expression_attribute_values) 42 | 43 | article_content_edit_table.update_item( 44 | Key={ 45 | 'article_id': self.params['article_id'], 46 | }, 47 | UpdateExpression="set user_id=:user_id, title=:title", 48 | ExpressionAttributeValues=expression_attribute_values 49 | ) 50 | 51 | return { 52 | 'statusCode': 200 53 | } 54 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/unpublish/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_public_unpublish import MeArticlesPublicUnpublish 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_public_unpublish = MeArticlesPublicUnpublish(event, context, dynamodb) 10 | return me_articles_public_unpublish.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/unpublish/me_articles_public_unpublish.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import settings 4 | from lambda_base import LambdaBase 5 | from jsonschema import validate 6 | from db_util import DBUtil 7 | from user_util import UserUtil 8 | 9 | 10 | class MeArticlesPublicUnpublish(LambdaBase): 11 | def get_schema(self): 12 | return { 13 | 'type': 'object', 14 | 'properties': { 15 | 'article_id': settings.parameters['article_id'], 16 | }, 17 | 'required': ['article_id'] 18 | } 19 | 20 | def validate_params(self): 21 | UserUtil.verified_phone_and_email(self.event) 22 | validate(self.params, self.get_schema()) 23 | 24 | DBUtil.validate_article_existence( 25 | self.dynamodb, 26 | self.params['article_id'], 27 | user_id=self.event['requestContext']['authorizer']['claims']['cognito:username'], 28 | status='public' 29 | ) 30 | 31 | def exec_main_proc(self): 32 | self.__delete_article_content_edit() 33 | 34 | article_info_table = self.dynamodb.Table(os.environ['ARTICLE_INFO_TABLE_NAME']) 35 | 36 | article_info_table.update_item( 37 | Key={ 38 | 'article_id': self.params['article_id'], 39 | }, 40 | UpdateExpression='set #attr = :article_status, #sync_elasticsearch = :one', 41 | ExpressionAttributeNames={ 42 | '#attr': 'status', 43 | '#sync_elasticsearch': 'sync_elasticsearch' 44 | }, 45 | ExpressionAttributeValues={':article_status': 'draft', ':one': 1} 46 | ) 47 | 48 | return { 49 | 'statusCode': 200 50 | } 51 | 52 | def __delete_article_content_edit(self): 53 | article_content_edit_table = self.dynamodb.Table(os.environ['ARTICLE_CONTENT_EDIT_TABLE_NAME']) 54 | article_content_edit = article_content_edit_table.get_item(Key={'article_id': self.params['article_id']}).get('Item') 55 | 56 | if article_content_edit: 57 | article_content_edit_table.delete_item(Key={'article_id': self.params['article_id']}) 58 | -------------------------------------------------------------------------------- /src/handlers/me/articles/public/update/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_public_update import MeArticlesPublicUpdate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_public_update = MeArticlesPublicUpdate(event, context, dynamodb) 10 | return me_articles_public_update.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/purchase/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_purchase_create import MeArticlesPurchaseCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | cognito = boto3.client('cognito-idp') 7 | 8 | 9 | def lambda_handler(event, context): 10 | me_articles_purchase_create = MeArticlesPurchaseCreate(event=event, context=context, dynamodb=dynamodb, cognito=cognito) 11 | return me_articles_purchase_create.main() 12 | -------------------------------------------------------------------------------- /src/handlers/me/articles/purchased/article_ids/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_purchased_article_ids_index import MeArticlesPurchasedArticleIdsIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_purchased_article_ids_index = MeArticlesPurchasedArticleIdsIndex(event=event, context=context, 10 | dynamodb=dynamodb) 11 | return me_articles_purchased_article_ids_index.main() 12 | -------------------------------------------------------------------------------- /src/handlers/me/articles/purchased/article_ids/index/me_articles_purchased_article_ids_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | 5 | from boto3.dynamodb.conditions import Key 6 | from db_util import DBUtil 7 | from lambda_base import LambdaBase 8 | 9 | 10 | class MeArticlesPurchasedArticleIdsIndex(LambdaBase): 11 | def get_schema(self): 12 | pass 13 | 14 | def validate_params(self): 15 | pass 16 | 17 | def exec_main_proc(self): 18 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 19 | paid_articles_table = self.dynamodb.Table(os.environ['PAID_ARTICLES_TABLE_NAME']) 20 | 21 | query_params = { 22 | 'IndexName': 'user_id-sort_key-index', 23 | 'KeyConditionExpression': Key('user_id').eq(user_id) 24 | } 25 | 26 | result = DBUtil.query_all_items(paid_articles_table, query_params) 27 | 28 | article_ids = [paid_article['article_id'] for paid_article in result if 29 | paid_article['user_id'] == user_id and paid_article['status'] == 'done'] 30 | 31 | return { 32 | 'statusCode': 200, 33 | 'body': json.dumps({'article_ids': article_ids}) 34 | } 35 | -------------------------------------------------------------------------------- /src/handlers/me/articles/purchased/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_purchased_index import MeArticlesPurchasedIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_purchased_index = MeArticlesPurchasedIndex(event, context, dynamodb) 10 | return me_articles_purchased_index.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/purchased/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_purchased_show import MeArticlesPurchasedShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_purchased_show = MeArticlesPurchasedShow(event, context, dynamodb) 10 | return me_articles_purchased_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/articles/purchased/show/me_articles_purchased_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | import settings 5 | from lambda_base import LambdaBase 6 | from jsonschema import validate 7 | from decimal_encoder import DecimalEncoder 8 | from db_util import DBUtil 9 | from not_authorized_error import NotAuthorizedError 10 | from boto3.dynamodb.conditions import Key 11 | 12 | 13 | class MeArticlesPurchasedShow(LambdaBase): 14 | def get_schema(self): 15 | return { 16 | 'type': 'object', 17 | 'properties': { 18 | 'article_id': settings.parameters['article_id'] 19 | }, 20 | 'required': ['article_id'] 21 | } 22 | 23 | def validate_params(self): 24 | validate(self.params, self.get_schema()) 25 | 26 | DBUtil.validate_article_existence( 27 | self.dynamodb, 28 | self.params['article_id'], 29 | status='public', 30 | ) 31 | 32 | def exec_main_proc(self): 33 | article_info_table = self.dynamodb.Table(os.environ['ARTICLE_INFO_TABLE_NAME']) 34 | article_content_table = self.dynamodb.Table(os.environ['ARTICLE_CONTENT_TABLE_NAME']) 35 | paid_articles_table = self.dynamodb.Table(os.environ['PAID_ARTICLES_TABLE_NAME']) 36 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 37 | 38 | paid_articles = paid_articles_table.query( 39 | IndexName='article_id-user_id-index', 40 | KeyConditionExpression=Key('article_id').eq(self.params['article_id']) & Key('user_id').eq(user_id), 41 | ).get('Items') 42 | 43 | if len([i for i in paid_articles if i.get('status') == 'done']) != 1: 44 | raise NotAuthorizedError('Forbidden') 45 | 46 | article_info = article_info_table.get_item(Key={'article_id': self.params['article_id']}).get('Item') 47 | article_content = article_content_table.get_item(Key={'article_id': self.params['article_id']}).get('Item') 48 | 49 | # 記事が有料から無料になるケースを考慮し、無料記事の場合は本文(body)をそのまま返却する 50 | if 'price' in article_info: 51 | article_content['body'] = article_content['paid_body'] 52 | article_content.pop('paid_body', None) 53 | 54 | article_info.update(article_content) 55 | 56 | return { 57 | 'statusCode': 200, 58 | 'body': json.dumps(article_info, cls=DecimalEncoder) 59 | } 60 | -------------------------------------------------------------------------------- /src/handlers/me/articles/pv/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_articles_pv_create import MeArticlesPvCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_articles_pv_create = MeArticlesPvCreate(event=event, context=context, dynamodb=dynamodb) 10 | return me_articles_pv_create.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/comments/delete/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_comments_delete import MeCommentsDelete 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_comments_delete = MeCommentsDelete(event=event, context=context, dynamodb=dynamodb) 10 | return me_comments_delete.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/comments/likes/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_comments_likes_create import MeCommentsLikesCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_comments_likes_create = MeCommentsLikesCreate(event=event, context=context, dynamodb=dynamodb) 10 | return me_comments_likes_create.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/comments/likes/create/me_comments_likes_create.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | import settings 5 | import time 6 | 7 | from botocore.exceptions import ClientError 8 | from db_util import DBUtil 9 | from lambda_base import LambdaBase 10 | from jsonschema import validate 11 | from user_util import UserUtil 12 | 13 | 14 | class MeCommentsLikesCreate(LambdaBase): 15 | def get_schema(self): 16 | return { 17 | 'type': 'object', 18 | 'properties': { 19 | 'comment_id': settings.parameters['comment']['comment_id'] 20 | }, 21 | 'required': ['comment_id'] 22 | } 23 | 24 | def validate_params(self): 25 | UserUtil.verified_phone_and_email(self.event) 26 | validate(self.params, self.get_schema()) 27 | comment = DBUtil.get_validated_comment(self.dynamodb, self.params['comment_id']) 28 | DBUtil.validate_article_existence(self.dynamodb, comment['article_id'], status='public') 29 | 30 | def exec_main_proc(self): 31 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 32 | 33 | comment_table = self.dynamodb.Table(os.environ['COMMENT_TABLE_NAME']) 34 | comment = comment_table.get_item(Key={'comment_id': self.params['comment_id']}).get('Item') 35 | 36 | comment_liked_user_table = self.dynamodb.Table(os.environ['COMMENT_LIKED_USER_TABLE_NAME']) 37 | comment_liked_user = { 38 | 'comment_id': comment['comment_id'], 39 | 'user_id': user_id, 40 | 'article_id': comment['article_id'], 41 | 'created_at': int(time.time()) 42 | } 43 | 44 | try: 45 | comment_liked_user_table.put_item( 46 | Item=comment_liked_user, 47 | ConditionExpression='attribute_not_exists(comment_id)' 48 | ) 49 | except ClientError as e: 50 | if e.response['Error']['Code'] == 'ConditionalCheckFailedException': 51 | return { 52 | 'statusCode': 400, 53 | 'body': json.dumps({'message': 'Already exists'}) 54 | } 55 | else: 56 | raise 57 | 58 | return {'statusCode': 200} 59 | -------------------------------------------------------------------------------- /src/handlers/me/configurations/mute_users/add/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_configurations_mute_users_add import MeConfigurationsMuteUsersAdd 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_configurations_mute_users_add = MeConfigurationsMuteUsersAdd(event, context, dynamodb) 10 | return me_configurations_mute_users_add.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/configurations/mute_users/add/me_configurations_mute_users_add.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import settings 4 | from db_util import DBUtil 5 | from lambda_base import LambdaBase 6 | from jsonschema import validate, ValidationError 7 | from exceptions import LimitExceeded 8 | 9 | 10 | class MeConfigurationsMuteUsersAdd(LambdaBase): 11 | def get_schema(self): 12 | return { 13 | 'type': 'object', 14 | 'properties': { 15 | 'mute_user_id': settings.parameters['user_id'] 16 | }, 17 | 'required': ['mute_user_id'] 18 | } 19 | 20 | def validate_params(self): 21 | # single 22 | validate(self.params, self.get_schema()) 23 | # relation 24 | DBUtil.validate_user_existence( 25 | self.dynamodb, 26 | self.params['mute_user_id'] 27 | ) 28 | # 自分自身でないことを確認 29 | if self.params['mute_user_id'] == self.event['requestContext']['authorizer']['claims']['cognito:username']: 30 | raise ValidationError(self.params['mute_user_id'] + ' is own user.') 31 | 32 | def exec_main_proc(self): 33 | user_configurations_table = self.dynamodb.Table(os.environ['USER_CONFIGURATIONS_TABLE_NAME']) 34 | 35 | # 登録数制限の確認 36 | user_configurations = user_configurations_table.get_item(Key={ 37 | 'user_id': self.event['requestContext']['authorizer']['claims']['cognito:username'] 38 | }) 39 | if user_configurations.get('Item') is not None and \ 40 | user_configurations.get('Item').get('mute_users') is not None and \ 41 | len(user_configurations.get('Item').get('mute_users')) >= settings.MUTE_USERS_MAX_COUNT: 42 | raise LimitExceeded('mute users') 43 | 44 | # ミュートユーザ追加(mute_users 項目として set で管理する) 45 | user_configurations_table.update_item( 46 | Key={ 47 | 'user_id': self.event['requestContext']['authorizer']['claims']['cognito:username'], 48 | }, 49 | UpdateExpression="add mute_users :mute_users", 50 | ExpressionAttributeValues={':mute_users': {self.params['mute_user_id']}} 51 | ) 52 | 53 | return { 54 | 'statusCode': 200 55 | } 56 | -------------------------------------------------------------------------------- /src/handlers/me/configurations/mute_users/delete/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_configurations_mute_users_delete import MeConfigurationsMuteUsersDelete 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_configurations_mute_users_delete = MeConfigurationsMuteUsersDelete(event, context, dynamodb) 10 | return me_configurations_mute_users_delete.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/configurations/mute_users/delete/me_configurations_mute_users_delete.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import settings 4 | from db_util import DBUtil 5 | from lambda_base import LambdaBase 6 | from jsonschema import validate 7 | 8 | 9 | class MeConfigurationsMuteUsersDelete(LambdaBase): 10 | def get_schema(self): 11 | return { 12 | 'type': 'object', 13 | 'properties': { 14 | 'mute_user_id': settings.parameters['user_id'] 15 | }, 16 | 'required': ['mute_user_id'] 17 | } 18 | 19 | def validate_params(self): 20 | # single 21 | validate(self.params, self.get_schema()) 22 | # relation 23 | DBUtil.validate_user_existence( 24 | self.dynamodb, 25 | self.params['mute_user_id'] 26 | ) 27 | 28 | def exec_main_proc(self): 29 | user_configurations_table = self.dynamodb.Table(os.environ['USER_CONFIGURATIONS_TABLE_NAME']) 30 | 31 | # ミュートユーザ削除 32 | user_configurations_table.update_item( 33 | Key={ 34 | 'user_id': self.event['requestContext']['authorizer']['claims']['cognito:username'], 35 | }, 36 | UpdateExpression="delete mute_users :mute_users", 37 | ExpressionAttributeValues={':mute_users': {self.params['mute_user_id']}} 38 | ) 39 | 40 | return { 41 | 'statusCode': 200 42 | } 43 | -------------------------------------------------------------------------------- /src/handlers/me/configurations/mute_users/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_configurations_mute_users_index import MeConfigurationsMuteUsersIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_configurations_mute_users_index = MeConfigurationsMuteUsersIndex(event, context, dynamodb) 10 | return me_configurations_mute_users_index.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/configurations/mute_users/index/me_configurations_mute_users_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | from lambda_base import LambdaBase 5 | 6 | 7 | class MeConfigurationsMuteUsersIndex(LambdaBase): 8 | def get_schema(self): 9 | pass 10 | 11 | def validate_params(self): 12 | pass 13 | 14 | def exec_main_proc(self): 15 | user_configurations_table = self.dynamodb.Table(os.environ['USER_CONFIGURATIONS_TABLE_NAME']) 16 | user_configurations = user_configurations_table.get_item( 17 | Key={'user_id': self.event['requestContext']['authorizer']['claims']['cognito:username']}) 18 | 19 | mute_users = [] 20 | if user_configurations.get('Item') is not None and \ 21 | user_configurations.get('Item').get('mute_users') is not None: 22 | # mute_users は set形 のため list形 へ変更 23 | mute_users = list(user_configurations.get('Item').get('mute_users')) 24 | # user_id の昇順でソート 25 | mute_users.sort() 26 | 27 | return_body = { 28 | 'mute_users': mute_users 29 | } 30 | 31 | return { 32 | 'statusCode': 200, 33 | 'body': json.dumps(return_body) 34 | } 35 | -------------------------------------------------------------------------------- /src/handlers/me/configurations/wallet/add/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_configurations_wallet_add import MeConfigurationsWalletAdd 4 | 5 | cognito = boto3.client('cognito-idp') 6 | dynamodb = boto3.resource('dynamodb') 7 | 8 | 9 | def lambda_handler(event, context): 10 | me_configurations_wallet_add = MeConfigurationsWalletAdd(event, context, dynamodb, cognito=cognito) 11 | return me_configurations_wallet_add.main() 12 | -------------------------------------------------------------------------------- /src/handlers/me/configurations/wallet/show/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from me_configurations_wallet_show import MeConfigurationsWalletShow 3 | 4 | dynamodb = boto3.resource('dynamodb') 5 | 6 | 7 | def lambda_handler(event, context): 8 | me_configurations_wallet_show = MeConfigurationsWalletShow(event, context, dynamodb) 9 | return me_configurations_wallet_show.main() 10 | -------------------------------------------------------------------------------- /src/handlers/me/configurations/wallet/show/me_configurations_wallet_show.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | from lambda_base import LambdaBase 4 | 5 | 6 | class MeConfigurationsWalletShow(LambdaBase): 7 | def get_schema(self): 8 | pass 9 | 10 | def validate_params(self): 11 | pass 12 | 13 | def exec_main_proc(self): 14 | user_configurations_table = self.dynamodb.Table(os.environ['USER_CONFIGURATIONS_TABLE_NAME']) 15 | user_configurations = user_configurations_table.get_item( 16 | Key={'user_id': self.event['requestContext']['authorizer']['claims']['cognito:username']}) 17 | 18 | return_body = {} 19 | if user_configurations.get('Item') is not None and \ 20 | user_configurations.get('Item').get('private_eth_address') is not None: 21 | return_body['wallet_address'] = user_configurations.get('Item')['private_eth_address'] 22 | return_body['salt'] = user_configurations.get('Item')['salt'] 23 | return_body['encrypted_secret_key'] = user_configurations.get('Item')['encrypted_secret_key'] 24 | 25 | return { 26 | 'statusCode': 200, 27 | 'body': json.dumps(return_body) 28 | } 29 | -------------------------------------------------------------------------------- /src/handlers/me/external_provider_user/create/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from me_external_provider_user_create import MeExternalProviderUserCreate 3 | 4 | dynamodb = boto3.resource('dynamodb') 5 | cognito = boto3.client('cognito-idp') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_external_provider_user_create = MeExternalProviderUserCreate( 10 | event=event, 11 | context=context, 12 | dynamodb=dynamodb, 13 | cognito=cognito 14 | ) 15 | return me_external_provider_user_create.main() 16 | -------------------------------------------------------------------------------- /src/handlers/me/info/first_experiences/update/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_info_first_experiences_update import MeInfoFirstExperiencesUpdate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_info_first_experiences_update = MeInfoFirstExperiencesUpdate(event=event, context=context, dynamodb=dynamodb) 10 | return me_info_first_experiences_update.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/info/first_experiences/update/me_info_first_experiences_update.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from jsonschema import validate 4 | 5 | import settings 6 | from lambda_base import LambdaBase 7 | 8 | 9 | class MeInfoFirstExperiencesUpdate(LambdaBase): 10 | 11 | def get_schema(self): 12 | return { 13 | 'type': 'object', 14 | 'properties': { 15 | 'user_first_experience': settings.parameters['user_first_experience'] 16 | }, 17 | 'required': ['user_first_experience'] 18 | } 19 | 20 | def validate_params(self): 21 | validate(self.params, self.get_schema()) 22 | 23 | def exec_main_proc(self): 24 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 25 | 26 | table = self.dynamodb.Table(os.environ['USER_FIRST_EXPERIENCE_TABLE_NAME']) 27 | table.update_item( 28 | Key={'user_id': user_id}, 29 | UpdateExpression='set #key = :true', 30 | ExpressionAttributeNames={'#key': self.params['user_first_experience']}, 31 | ExpressionAttributeValues={':true': True} 32 | ) 33 | 34 | return {'statusCode': 200} 35 | -------------------------------------------------------------------------------- /src/handlers/me/info/icon/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_info_icon_create import MeInfoIconCreate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | s3 = boto3.resource('s3') 7 | 8 | 9 | def lambda_handler(event, context): 10 | me_info_icon_create = MeInfoIconCreate(event=event, context=context, dynamodb=dynamodb, s3=s3) 11 | return me_info_icon_create.main() 12 | -------------------------------------------------------------------------------- /src/handlers/me/info/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_info_show import MeInfoShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_info_show = MeInfoShow(event=event, context=context, dynamodb=dynamodb) 10 | return me_info_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/info/show/me_info_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | from lambda_base import LambdaBase 5 | from decimal_encoder import DecimalEncoder 6 | from record_not_found_error import RecordNotFoundError 7 | 8 | 9 | class MeInfoShow(LambdaBase): 10 | def get_schema(self): 11 | pass 12 | 13 | def validate_params(self): 14 | pass 15 | 16 | def exec_main_proc(self): 17 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 18 | users_table = self.dynamodb.Table(os.environ['USERS_TABLE_NAME']) 19 | 20 | user = users_table.get_item(Key={'user_id': user_id}).get('Item') 21 | 22 | if user is None: 23 | raise RecordNotFoundError('Record Not Found') 24 | 25 | user_first_experience_table = self.dynamodb.Table(os.environ['USER_FIRST_EXPERIENCE_TABLE_NAME']) 26 | experience = user_first_experience_table.get_item(Key={'user_id': user_id}).get('Item') 27 | if experience: 28 | user.update(experience) 29 | 30 | return { 31 | 'statusCode': 200, 32 | 'body': json.dumps(user, cls=DecimalEncoder) 33 | } 34 | -------------------------------------------------------------------------------- /src/handlers/me/info/update/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_info_update import MeInfoUpdate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_info_update = MeInfoUpdate(event, context, dynamodb) 10 | return me_info_update.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/info/update/me_info_update.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import settings 4 | from db_util import DBUtil 5 | from lambda_base import LambdaBase 6 | from jsonschema import validate 7 | from text_sanitizer import TextSanitizer 8 | 9 | 10 | class MeInfoUpdate(LambdaBase): 11 | def get_schema(self): 12 | return { 13 | 'type': 'object', 14 | 'properties': { 15 | 'user_display_name': settings.parameters['user_display_name'], 16 | 'self_introduction': settings.parameters['self_introduction'] 17 | }, 18 | 'required': ['user_display_name', 'self_introduction'] 19 | } 20 | 21 | def validate_params(self): 22 | # single 23 | validate(self.params, self.get_schema()) 24 | # relation 25 | DBUtil.validate_user_existence( 26 | self.dynamodb, 27 | self.event['requestContext']['authorizer']['claims']['cognito:username'] 28 | ) 29 | 30 | def exec_main_proc(self): 31 | users_table = self.dynamodb.Table(os.environ['USERS_TABLE_NAME']) 32 | 33 | expression_attribute_values = { 34 | ':user_display_name': TextSanitizer.sanitize_text(self.params['user_display_name']), 35 | ':self_introduction': TextSanitizer.sanitize_text(self.params['self_introduction']), 36 | ':sync_elasticsearch': 1 37 | } 38 | DBUtil.items_values_empty_to_none(expression_attribute_values) 39 | 40 | users_table.update_item( 41 | Key={ 42 | 'user_id': self.event['requestContext']['authorizer']['claims']['cognito:username'], 43 | }, 44 | UpdateExpression=("set user_display_name=:user_display_name, self_introduction=:self_introduction, " 45 | "sync_elasticsearch=:sync_elasticsearch"), 46 | ExpressionAttributeValues=expression_attribute_values 47 | ) 48 | 49 | return { 50 | 'statusCode': 200 51 | } 52 | -------------------------------------------------------------------------------- /src/handlers/me/notifications/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_notifications_index import MeNotificationsIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_notifications_index = MeNotificationsIndex(event=event, context=context, dynamodb=dynamodb) 10 | return me_notifications_index.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/notifications/index/me_notifications_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import settings 4 | import json 5 | from decimal_encoder import DecimalEncoder 6 | from boto3.dynamodb.conditions import Key 7 | from parameter_util import ParameterUtil 8 | from lambda_base import LambdaBase 9 | from jsonschema import validate 10 | 11 | 12 | class MeNotificationsIndex(LambdaBase): 13 | def get_schema(self): 14 | return { 15 | 'type': 'object', 16 | 'properties': { 17 | 'limit': settings.parameters['limit'], 18 | 'notification_id': settings.parameters['notification_id'], 19 | 'sort_key': settings.parameters['sort_key'] 20 | } 21 | } 22 | 23 | def validate_params(self): 24 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 25 | validate(self.params, self.get_schema()) 26 | 27 | def exec_main_proc(self): 28 | notification_table = self.dynamodb.Table(os.environ['NOTIFICATION_TABLE_NAME']) 29 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 30 | 31 | limit = settings.NOTIFICATION_INDEX_DEFAULT_LIMIT 32 | if self.params.get('limit'): 33 | limit = int(self.params.get('limit')) 34 | 35 | query_params = { 36 | 'Limit': limit, 37 | 'IndexName': 'user_id-sort_key-index', 38 | 'KeyConditionExpression': Key('user_id').eq(user_id), 39 | 'ScanIndexForward': False 40 | } 41 | 42 | if self.params.get('notification_id') is not None and self.params.get('sort_key') is not None: 43 | LastEvaluatedKey = { 44 | 'notification_id': self.params.get('notification_id'), 45 | 'user_id': user_id, 46 | 'sort_key': int(self.params['sort_key']) 47 | } 48 | 49 | query_params.update({'ExclusiveStartKey': LastEvaluatedKey}) 50 | 51 | response = notification_table.query(**query_params) 52 | 53 | return { 54 | 'statusCode': 200, 55 | 'body': json.dumps(response, cls=DecimalEncoder) 56 | } 57 | -------------------------------------------------------------------------------- /src/handlers/me/unread_notification_managers/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_unread_notification_managers_show import MeUnreadNotificationManagersShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_unread_notification_managers_show = MeUnreadNotificationManagersShow(event=event, context=context, dynamodb=dynamodb) 10 | return me_unread_notification_managers_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/unread_notification_managers/show/me_unread_notification_managers_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | from lambda_base import LambdaBase 5 | 6 | 7 | class MeUnreadNotificationManagersShow(LambdaBase): 8 | def get_schema(self): 9 | pass 10 | 11 | def validate_params(self): 12 | pass 13 | 14 | def exec_main_proc(self): 15 | unread_notification_manager_table = self.dynamodb.Table(os.environ['UNREAD_NOTIFICATION_MANAGER_TABLE_NAME']) 16 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 17 | 18 | manager = unread_notification_manager_table.get_item(Key={'user_id': user_id}).get('Item') 19 | 20 | unread = True if manager and manager['unread'] else False 21 | 22 | return { 23 | 'statusCode': 200, 24 | 'body': json.dumps({'unread': unread}) 25 | } 26 | -------------------------------------------------------------------------------- /src/handlers/me/unread_notification_managers/update/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_unread_notification_managers_update import MeUnreadNotificationManagersUpdate 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | me_unread_notification_managers_update = MeUnreadNotificationManagersUpdate(event=event, context=context, dynamodb=dynamodb) 10 | return me_unread_notification_managers_update.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/unread_notification_managers/update/me_unread_notification_managers_update.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from lambda_base import LambdaBase 4 | 5 | 6 | class MeUnreadNotificationManagersUpdate(LambdaBase): 7 | def get_schema(self): 8 | pass 9 | 10 | def validate_params(self): 11 | pass 12 | 13 | def exec_main_proc(self): 14 | unread_notification_manager_table = self.dynamodb.Table(os.environ['UNREAD_NOTIFICATION_MANAGER_TABLE_NAME']) 15 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 16 | 17 | unread_notification_manager_table.update_item( 18 | Key={'user_id': user_id}, 19 | UpdateExpression='set unread = :unread', 20 | ExpressionAttributeValues={':unread': False} 21 | ) 22 | 23 | return { 24 | 'statusCode': 200 25 | } 26 | -------------------------------------------------------------------------------- /src/handlers/me/users/fraud/create/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from me_users_fraud_create import MeUsersFraudCreate 3 | 4 | dynamodb = boto3.resource('dynamodb') 5 | 6 | 7 | def lambda_handler(event, context): 8 | me_users_fraud_create = MeUsersFraudCreate(event=event, context=context, dynamodb=dynamodb) 9 | return me_users_fraud_create.main() 10 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/allowance/show/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from me_wallet_allowance_show import MeWalletAllowanceShow 3 | 4 | dynamodb = boto3.resource('dynamodb') 5 | 6 | 7 | def lambda_handler(event, context): 8 | me_wallet_allowance_show = MeWalletAllowanceShow(event, context, dynamodb) 9 | return me_wallet_allowance_show.main() 10 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/allowance/show/me_wallet_allowance_show.py: -------------------------------------------------------------------------------- 1 | import json 2 | from private_chain_util import PrivateChainUtil 3 | from lambda_base import LambdaBase 4 | 5 | 6 | class MeWalletAllowanceShow(LambdaBase): 7 | 8 | def get_schema(self): 9 | pass 10 | 11 | def validate_params(self): 12 | pass 13 | 14 | def exec_main_proc(self): 15 | from_user_eth_address = self.event['requestContext']['authorizer']['claims'].get('custom:private_eth_address') 16 | 17 | # allowance を取得 18 | # まだウォレットアドレスを作成していないユーザには 0 を返す 19 | if from_user_eth_address is None: 20 | allowance = '0x0' 21 | else: 22 | allowance = PrivateChainUtil.get_allowance(from_user_eth_address) 23 | 24 | return { 25 | 'statusCode': 200, 26 | 'body': json.dumps({'allowance': allowance}) 27 | } 28 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/balance/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_wallet_balance import MeWalletBalance 4 | 5 | cognito = boto3.client('cognito-idp') 6 | dynamodb = boto3.resource('dynamodb') 7 | 8 | 9 | def lambda_handler(event, context): 10 | me_wallet_balance = MeWalletBalance(event, context, dynamodb, cognito=cognito) 11 | return me_wallet_balance.main() 12 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/balance/me_wallet_balance.py: -------------------------------------------------------------------------------- 1 | import json 2 | from private_chain_util import PrivateChainUtil 3 | from lambda_base import LambdaBase 4 | from user_util import UserUtil 5 | 6 | 7 | class MeWalletBalance(LambdaBase): 8 | def get_schema(self): 9 | pass 10 | 11 | def validate_params(self): 12 | pass 13 | 14 | def exec_main_proc(self): 15 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 16 | address = self.event['requestContext']['authorizer']['claims'].get('custom:private_eth_address') 17 | 18 | # 現在のトークン量を取得 19 | # まだウォレットアドレスを作成していないユーザには 0 を返す 20 | if address is None: 21 | balance = '0x0' 22 | elif not UserUtil.exists_private_eth_address(self.dynamodb, user_id): 23 | # Cognito上にprivate_eth_addressは存在するが、カストディ規制のウォレット移行が完了していないユーザにも0を返す 24 | balance = '0x0' 25 | else: 26 | balance = PrivateChainUtil.get_balance(address) 27 | 28 | return { 29 | 'statusCode': 200, 30 | 'body': json.dumps({'result': balance}) 31 | } 32 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/distributed_tokens/show/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | 3 | from me_wallet_distributed_tokens_show import MeWalletDistributedTokensShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | target = MeWalletDistributedTokensShow(event, context, dynamodb=dynamodb) 10 | return target.main() 11 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/distributed_tokens/show/me_wallet_distributed_tokens_show.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | from boto3.dynamodb.conditions import Key 5 | 6 | from db_util import DBUtil 7 | from decimal_encoder import DecimalEncoder 8 | from lambda_base import LambdaBase 9 | 10 | 11 | class MeWalletDistributedTokensShow(LambdaBase): 12 | def get_schema(self): 13 | pass 14 | 15 | def validate_params(self): 16 | pass 17 | 18 | def exec_main_proc(self): 19 | user_id = self.event['requestContext']['authorizer']['claims']['cognito:username'] 20 | 21 | token_distribution_table = self.dynamodb.Table(os.environ['TOKEN_DISTRIBUTION_TABLE_NAME']) 22 | 23 | query_params = { 24 | 'IndexName': 'user_id-sort_key-index', 25 | 'KeyConditionExpression': Key('user_id').eq(user_id) 26 | } 27 | items = DBUtil.query_all_items(token_distribution_table, query_params) 28 | 29 | result = { 30 | 'article': 0, 31 | 'like': 0, 32 | 'tip': 0, 33 | 'bonus': 0 34 | } 35 | for item in items: 36 | result[item['distribution_type']] += item['quantity'] 37 | 38 | return { 39 | 'statusCode': 200, 40 | 'body': json.dumps(result, cls=DecimalEncoder) 41 | } 42 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/nonce/show/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from me_wallet_nonce_show import MeWalletNonceShow 3 | 4 | dynamodb = boto3.resource('dynamodb') 5 | 6 | 7 | def lambda_handler(event, context): 8 | me_wallet_nonce_show = MeWalletNonceShow(event, context, dynamodb) 9 | return me_wallet_nonce_show.main() 10 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/nonce/show/me_wallet_nonce_show.py: -------------------------------------------------------------------------------- 1 | import json 2 | from lambda_base import LambdaBase 3 | from private_chain_util import PrivateChainUtil 4 | 5 | 6 | class MeWalletNonceShow(LambdaBase): 7 | def get_schema(self): 8 | pass 9 | 10 | def validate_params(self): 11 | pass 12 | 13 | def exec_main_proc(self): 14 | address = self.event['requestContext']['authorizer']['claims'].get('custom:private_eth_address') 15 | 16 | # nonce を取得 17 | if address is None: 18 | # まだウォレットアドレスを作成していないユーザには 0 を返す 19 | nonce = '0x0' 20 | else: 21 | nonce = PrivateChainUtil.get_transaction_count(address) 22 | 23 | return { 24 | 'statusCode': 200, 25 | 'body': json.dumps({'nonce': nonce}) 26 | } 27 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/tip/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_wallet_tip import MeWalletTip 4 | 5 | cognito = boto3.client('cognito-idp') 6 | dynamodb = boto3.resource('dynamodb') 7 | 8 | 9 | def lambda_handler(event, context): 10 | me_wallet_tip = MeWalletTip(event, context, dynamodb, cognito=cognito) 11 | return me_wallet_tip.main() 12 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/token/allhistories/create/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_wallet_token_allhistories_create import MeWalletTokenAllhistoriesCreate 4 | 5 | cognito = boto3.client('cognito-idp') 6 | dynamodb = boto3.resource('dynamodb') 7 | s3 = boto3.resource('s3') 8 | 9 | 10 | def lambda_handler(event, context): 11 | me_wallet_token_allhistories_create = MeWalletTokenAllhistoriesCreate(event=event, context=context, 12 | dynamodb=dynamodb, s3=s3, cognito=cognito) 13 | return me_wallet_token_allhistories_create.main() 14 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/token/histories/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_wallet_token_histories_index import MeWalletTokenHistoriesIndex 4 | 5 | cognito = boto3.client('cognito-idp') 6 | dynamodb = boto3.resource('dynamodb') 7 | 8 | 9 | def lambda_handler(event, context): 10 | me_wallet_token_histories_index = MeWalletTokenHistoriesIndex(event, context, dynamodb, cognito=cognito) 11 | return me_wallet_token_histories_index.main() 12 | -------------------------------------------------------------------------------- /src/handlers/me/wallet/token/send/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from me_wallet_token_send import MeWalletTokenSend 4 | 5 | cognito = boto3.client('cognito-idp') 6 | dynamodb = boto3.resource('dynamodb') 7 | 8 | 9 | def lambda_handler(event, context): 10 | me_wallet_token_send = MeWalletTokenSend(event, context, dynamodb, cognito=cognito) 11 | return me_wallet_token_send.main() 12 | -------------------------------------------------------------------------------- /src/handlers/search/articles/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | import os 4 | from search_articles import SearchArticles 5 | from elasticsearch import Elasticsearch, RequestsHttpConnection 6 | from requests_aws4auth import AWS4Auth 7 | 8 | dynamodb = boto3.resource('dynamodb') 9 | awsauth = AWS4Auth( 10 | os.environ['AWS_ACCESS_KEY_ID'], 11 | os.environ['AWS_SECRET_ACCESS_KEY'], 12 | os.environ['AWS_REGION'], 13 | 'es', 14 | session_token=os.environ['AWS_SESSION_TOKEN'] 15 | ) 16 | elasticsearch = Elasticsearch( 17 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 18 | http_auth=awsauth, 19 | use_ssl=True, 20 | verify_certs=True, 21 | connection_class=RequestsHttpConnection 22 | ) 23 | 24 | 25 | def lambda_handler(event, context): 26 | search_articles = SearchArticles(event, context, dynamodb=dynamodb, elasticsearch=elasticsearch) 27 | return search_articles.main() 28 | -------------------------------------------------------------------------------- /src/handlers/search/articles/search_articles.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import settings 4 | from es_util import ESUtil 5 | from lambda_base import LambdaBase 6 | from jsonschema import validate 7 | from decimal_encoder import DecimalEncoder 8 | from parameter_util import ParameterUtil 9 | 10 | 11 | class SearchArticles(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'limit': settings.parameters['limit'], 17 | 'page': settings.parameters['page'], 18 | 'query': settings.parameters['query'], 19 | 'tag': settings.parameters['tag'] 20 | }, 21 | 'anyOf': [ 22 | {'required': ['query']}, 23 | {'required': ['tag']}, 24 | ] 25 | } 26 | 27 | def validate_params(self): 28 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 29 | validate(self.params, self.get_schema()) 30 | 31 | def exec_main_proc(self): 32 | query = self.params.get('query') 33 | tag = self.params.get('tag') 34 | limit = int(self.params.get('limit')) if self.params.get('limit') is not None else settings.article_recent_default_limit 35 | page = int(self.params.get('page')) if self.params.get('page') is not None else 1 36 | response = ESUtil.search_article(self.elasticsearch, limit, page, word=query, tag=tag) 37 | result = [] 38 | for a in response["hits"]["hits"]: 39 | del(a["_source"]["body"]) 40 | result.append(a["_source"]) 41 | return { 42 | 'statusCode': 200, 43 | 'body': json.dumps(result, cls=DecimalEncoder) 44 | } 45 | -------------------------------------------------------------------------------- /src/handlers/search/tags/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from search_tags import SearchTags 4 | from elasticsearch import Elasticsearch, RequestsHttpConnection 5 | from requests_aws4auth import AWS4Auth 6 | 7 | awsauth = AWS4Auth( 8 | os.environ['AWS_ACCESS_KEY_ID'], 9 | os.environ['AWS_SECRET_ACCESS_KEY'], 10 | os.environ['AWS_REGION'], 11 | 'es', 12 | session_token=os.environ['AWS_SESSION_TOKEN'] 13 | ) 14 | elasticsearch = Elasticsearch( 15 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 16 | http_auth=awsauth, 17 | use_ssl=True, 18 | verify_certs=True, 19 | connection_class=RequestsHttpConnection 20 | ) 21 | 22 | 23 | def lambda_handler(event, context): 24 | search_tags = SearchTags(event, context, elasticsearch=elasticsearch) 25 | return search_tags.main() 26 | -------------------------------------------------------------------------------- /src/handlers/search/tags/search_tags.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from jsonschema import validate 4 | 5 | import settings 6 | from decimal_encoder import DecimalEncoder 7 | from es_util import ESUtil 8 | from lambda_base import LambdaBase 9 | from parameter_util import ParameterUtil 10 | 11 | 12 | class SearchTags(LambdaBase): 13 | def get_schema(self): 14 | return { 15 | 'type': 'object', 16 | 'properties': { 17 | 'limit': settings.parameters['limit'], 18 | 'page': settings.parameters['page'], 19 | 'query': settings.parameters['query'] 20 | }, 21 | 'required': ['query'] 22 | } 23 | 24 | def validate_params(self): 25 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 26 | validate(self.params, self.get_schema()) 27 | 28 | def exec_main_proc(self): 29 | query = self.params['query'] 30 | limit = int(self.params.get('limit')) if self.params.get('limit') is not None else settings.TAG_SEARCH_DEFAULT_LIMIT 31 | page = int(self.params.get('page')) if self.params.get('page') is not None else 1 32 | 33 | result = ESUtil.search_tag(self.elasticsearch, query, limit, page) 34 | return { 35 | 'statusCode': 200, 36 | 'body': json.dumps(result, cls=DecimalEncoder) 37 | } 38 | -------------------------------------------------------------------------------- /src/handlers/search/tags_count/handler.py: -------------------------------------------------------------------------------- 1 | import os 2 | from search_tags_count import SearchTagsCount 3 | from elasticsearch import Elasticsearch, RequestsHttpConnection 4 | from requests_aws4auth import AWS4Auth 5 | 6 | awsauth = AWS4Auth( 7 | os.environ['AWS_ACCESS_KEY_ID'], 8 | os.environ['AWS_SECRET_ACCESS_KEY'], 9 | os.environ['AWS_REGION'], 10 | 'es', 11 | session_token=os.environ['AWS_SESSION_TOKEN'] 12 | ) 13 | elasticsearch = Elasticsearch( 14 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 15 | http_auth=awsauth, 16 | use_ssl=True, 17 | verify_certs=True, 18 | connection_class=RequestsHttpConnection 19 | ) 20 | 21 | 22 | def lambda_handler(event, context): 23 | search_tags_count = SearchTagsCount(event, context, elasticsearch=elasticsearch) 24 | return search_tags_count.main() 25 | -------------------------------------------------------------------------------- /src/handlers/search/tags_count/search_tags_count.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from jsonschema import validate 4 | 5 | import settings 6 | from decimal_encoder import DecimalEncoder 7 | from es_util import ESUtil 8 | from lambda_base import LambdaBase 9 | from parameter_util import ParameterUtil 10 | 11 | 12 | class SearchTagsCount(LambdaBase): 13 | def get_schema(self): 14 | return { 15 | 'type': 'object', 16 | 'properties': { 17 | 'tags': settings.parameters['tags_count'], 18 | 'search_days': settings.parameters['tags_count_search_days'] 19 | }, 20 | 'required': ['tags'] 21 | } 22 | 23 | def validate_params(self): 24 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 25 | self.params['tags'] = self.event['multiValueQueryStringParameters'].get('tags') 26 | validate(self.params, self.get_schema()) 27 | 28 | def exec_main_proc(self): 29 | # 直近1週間分のタグを集計 30 | search_size = len(self.params['tags']) * settings.parameters['tags']['maxItems'] 31 | search_days = self.params.get('search_days') if self.params.get('search_days') else 7 32 | from_time = 86400 * search_days 33 | search_result = ESUtil.search_tags_count(self.elasticsearch, self.params['tags'], search_size, from_time) 34 | 35 | # 集計結果より指定タグの件数を取得 36 | temp = [] 37 | for tag in self.params['tags']: 38 | tag_count = [d['doc_count'] for d in search_result if d['key'] == tag] 39 | temp.append({ 40 | 'tag': tag, 41 | 'count': tag_count[0] if len(tag_count) > 0 else 0 42 | }) 43 | result = sorted(temp, key=lambda x: x['count'], reverse=True) 44 | return { 45 | 'statusCode': 200, 46 | 'body': json.dumps(result, cls=DecimalEncoder) 47 | } 48 | -------------------------------------------------------------------------------- /src/handlers/search/users/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | import os 4 | from search_users import SearchUsers 5 | from elasticsearch import Elasticsearch, RequestsHttpConnection 6 | from requests_aws4auth import AWS4Auth 7 | 8 | dynamodb = boto3.resource('dynamodb') 9 | awsauth = AWS4Auth( 10 | os.environ['AWS_ACCESS_KEY_ID'], 11 | os.environ['AWS_SECRET_ACCESS_KEY'], 12 | os.environ['AWS_REGION'], 13 | 'es', 14 | session_token=os.environ['AWS_SESSION_TOKEN'] 15 | ) 16 | elasticsearch = Elasticsearch( 17 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 18 | http_auth=awsauth, 19 | use_ssl=True, 20 | verify_certs=True, 21 | connection_class=RequestsHttpConnection 22 | ) 23 | 24 | 25 | def lambda_handler(event, context): 26 | search_users = SearchUsers(event, context, dynamodb=dynamodb, elasticsearch=elasticsearch) 27 | return search_users.main() 28 | -------------------------------------------------------------------------------- /src/handlers/search/users/search_users.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import settings 4 | from es_util import ESUtil 5 | from lambda_base import LambdaBase 6 | from jsonschema import validate 7 | from decimal_encoder import DecimalEncoder 8 | from parameter_util import ParameterUtil 9 | 10 | 11 | class SearchUsers(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'limit': settings.parameters['limit'], 17 | 'page': settings.parameters['page'], 18 | 'query': settings.parameters['query'] 19 | }, 20 | 'required': ['query'] 21 | } 22 | 23 | def validate_params(self): 24 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 25 | validate(self.params, self.get_schema()) 26 | 27 | def exec_main_proc(self): 28 | query = self.params['query'] 29 | limit = int(self.params.get('limit')) if self.params.get('limit') is not None else settings.article_recent_default_limit 30 | page = int(self.params.get('page')) if self.params.get('page') is not None else 1 31 | response = ESUtil.search_user(self.elasticsearch, query, limit, page) 32 | result = [] 33 | for u in response["hits"]["hits"]: 34 | result.append(u["_source"]) 35 | return { 36 | 'statusCode': 200, 37 | 'body': json.dumps(result, cls=DecimalEncoder) 38 | } 39 | -------------------------------------------------------------------------------- /src/handlers/sign_up/line/authorize_url/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from sign_up_line_authorize_url import SignUpLineAuthorizeUrl 3 | 4 | cognito = boto3.client('cognito-idp') 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | sign_up_line_authorize_url = SignUpLineAuthorizeUrl(event=event, context=context, dynamodb=dynamodb, cognito=cognito) 10 | return sign_up_line_authorize_url.main() 11 | -------------------------------------------------------------------------------- /src/handlers/sign_up/line/authorize_url/sign_up_line_authorize_url.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import secrets 4 | import settings 5 | import json 6 | from lambda_base import LambdaBase 7 | 8 | 9 | class SignUpLineAuthorizeUrl(LambdaBase): 10 | def get_schema(self): 11 | pass 12 | 13 | def validate_params(self): 14 | pass 15 | 16 | def exec_main_proc(self): 17 | redirect_url = '&redirect_uri=' + os.environ['LINE_REDIRECT_URI'] 18 | state_and_scope = '&state=' + self.__generate_state() + settings.LINE_REQUEST_SCOPE 19 | url = settings.LINE_AUTHORIZE_URL + os.environ['LINE_CHANNEL_ID'] + redirect_url + state_and_scope 20 | 21 | return { 22 | 'statusCode': 200, 23 | 'body': json.dumps({ 24 | 'callback_url': url 25 | }) 26 | } 27 | 28 | @staticmethod 29 | def __generate_state(): 30 | return secrets.token_hex(4) 31 | -------------------------------------------------------------------------------- /src/handlers/topics/crypto/ranking/index/handler.py: -------------------------------------------------------------------------------- 1 | from topics_crypto_ranking_index import TopicsCryptoRankingIndex 2 | 3 | 4 | def lambda_handler(event, context): 5 | topics_crypto_ranking_index = TopicsCryptoRankingIndex(event=event, context=context) 6 | return topics_crypto_ranking_index.main() 7 | -------------------------------------------------------------------------------- /src/handlers/topics/crypto/ranking/index/topics_crypto_ranking_index.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import settings 4 | from jsonschema import validate 5 | from lambda_base import LambdaBase 6 | from parameter_util import ParameterUtil 7 | 8 | 9 | class TopicsCryptoRankingIndex(LambdaBase): 10 | def get_schema(self): 11 | return { 12 | 'type': 'object', 13 | 'properties': { 14 | 'limit': settings.parameters['limit'] 15 | } 16 | } 17 | 18 | def validate_params(self): 19 | ParameterUtil.cast_parameter_to_int(self.params, self.get_schema()) 20 | validate(self.params, self.get_schema()) 21 | 22 | def exec_main_proc(self): 23 | limit = int(self.params['limit']) if self.params.get('limit') else settings.CRYPTO_RAKING_DEFAULT_LIMIT 24 | try: 25 | response = requests.get( 26 | "https://api.coingecko.com/api/v3/coins/markets?vs_currency=jpy&order=market_cap_desc" 27 | f"&per_page={limit}" 28 | "&page=1&sparkline=false&price_change_percentage=24h" 29 | ) 30 | except Exception as err: 31 | raise Exception(f'Something went wrong when call CoinGecko API: {err}') 32 | 33 | return { 34 | 'statusCode': 200, 35 | 'body': json.dumps(self.extract_crypto_info(json.loads(response.text))) 36 | } 37 | 38 | # 利用する項目のみを抽出 39 | @staticmethod 40 | def extract_crypto_info(json_array): 41 | return [ 42 | { 43 | 'symbol': crypto_info['symbol'], 44 | 'name': crypto_info['name'], 45 | 'image': crypto_info['image'], 46 | 'current_price': crypto_info['current_price'], 47 | 'market_cap': crypto_info['market_cap'], 48 | 'price_change_percentage_24h': crypto_info['price_change_percentage_24h'] 49 | } 50 | for crypto_info in json_array 51 | ] 52 | -------------------------------------------------------------------------------- /src/handlers/topics/game/nft_games/ranking/index/handler.py: -------------------------------------------------------------------------------- 1 | import os 2 | import boto3 3 | from topics_game_nft_games_ranking_index import TopicsGameNftGamesRankingIndex 4 | from elasticsearch import Elasticsearch, RequestsHttpConnection 5 | from requests_aws4auth import AWS4Auth 6 | 7 | 8 | awsauth = AWS4Auth( 9 | os.environ['AWS_ACCESS_KEY_ID'], 10 | os.environ['AWS_SECRET_ACCESS_KEY'], 11 | os.environ['AWS_REGION'], 12 | 'es', 13 | session_token=os.environ['AWS_SESSION_TOKEN'] 14 | ) 15 | elasticsearch = Elasticsearch( 16 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 17 | http_auth=awsauth, 18 | use_ssl=True, 19 | verify_certs=True, 20 | connection_class=RequestsHttpConnection 21 | ) 22 | dynamodb = boto3.resource('dynamodb') 23 | 24 | 25 | def lambda_handler(event, context): 26 | topics_game_nft_games_ranking_index = TopicsGameNftGamesRankingIndex(event, context, dynamodb=dynamodb, 27 | elasticsearch=elasticsearch) 28 | return topics_game_nft_games_ranking_index.main() 29 | -------------------------------------------------------------------------------- /src/handlers/topics/game/nft_games/show/handler.py: -------------------------------------------------------------------------------- 1 | import os 2 | import boto3 3 | from topics_game_nft_games_show import TopicsGameNftGamesShow 4 | from elasticsearch import Elasticsearch, RequestsHttpConnection 5 | from requests_aws4auth import AWS4Auth 6 | 7 | 8 | awsauth = AWS4Auth( 9 | os.environ['AWS_ACCESS_KEY_ID'], 10 | os.environ['AWS_SECRET_ACCESS_KEY'], 11 | os.environ['AWS_REGION'], 12 | 'es', 13 | session_token=os.environ['AWS_SESSION_TOKEN'] 14 | ) 15 | elasticsearch = Elasticsearch( 16 | hosts=[{'host': os.environ['ELASTIC_SEARCH_ENDPOINT'], 'port': 443}], 17 | http_auth=awsauth, 18 | use_ssl=True, 19 | verify_certs=True, 20 | connection_class=RequestsHttpConnection 21 | ) 22 | dynamodb = boto3.resource('dynamodb') 23 | 24 | 25 | def lambda_handler(event, context): 26 | topics_game_nft_games_show = TopicsGameNftGamesShow(event, context, dynamodb=dynamodb, elasticsearch=elasticsearch) 27 | return topics_game_nft_games_show.main() 28 | -------------------------------------------------------------------------------- /src/handlers/topics/index/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from topics_index import TopicsIndex 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | topics_index = TopicsIndex(event=event, context=context, dynamodb=dynamodb) 10 | return topics_index.main() 11 | -------------------------------------------------------------------------------- /src/handlers/topics/index/topics_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | 5 | import settings 6 | from boto3.dynamodb.conditions import Key 7 | from decimal_encoder import DecimalEncoder 8 | from lambda_base import LambdaBase 9 | 10 | 11 | class TopicsIndex(LambdaBase): 12 | def get_schema(self): 13 | pass 14 | 15 | def validate_params(self): 16 | pass 17 | 18 | def exec_main_proc(self): 19 | topic_table = self.dynamodb.Table(os.environ['TOPIC_TABLE_NAME']) 20 | 21 | query_params = { 22 | 'IndexName': 'index_hash_key-order-index', 23 | 'KeyConditionExpression': Key('index_hash_key').eq(settings.TOPIC_INDEX_HASH_KEY) 24 | } 25 | 26 | topics = topic_table.query(**query_params)['Items'] 27 | 28 | return { 29 | 'statusCode': 200, 30 | 'body': json.dumps(topics, cls=DecimalEncoder) 31 | } 32 | -------------------------------------------------------------------------------- /src/handlers/users/articles/popular/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from users_articles_popular import UsersArticlesPopular 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | users_articles_popular = UsersArticlesPopular(event, context, dynamodb) 10 | return users_articles_popular.main() 11 | -------------------------------------------------------------------------------- /src/handlers/users/articles/public/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from users_articles_public import UsersArticlesPublic 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | users_articles_public = UsersArticlesPublic(event, context, dynamodb) 10 | return users_articles_public.main() 11 | -------------------------------------------------------------------------------- /src/handlers/users/info/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import boto3 3 | from users_info_show import UsersInfoShow 4 | 5 | dynamodb = boto3.resource('dynamodb') 6 | 7 | 8 | def lambda_handler(event, context): 9 | users_info_show = UsersInfoShow(event=event, context=context, dynamodb=dynamodb) 10 | return users_info_show.main() 11 | -------------------------------------------------------------------------------- /src/handlers/users/info/show/users_info_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | import settings 5 | from lambda_base import LambdaBase 6 | from jsonschema import validate 7 | from decimal_encoder import DecimalEncoder 8 | from record_not_found_error import RecordNotFoundError 9 | 10 | 11 | class UsersInfoShow(LambdaBase): 12 | def get_schema(self): 13 | return { 14 | 'type': 'object', 15 | 'properties': { 16 | 'user_id': settings.parameters['user_id'], 17 | }, 18 | 'required': ['user_id'] 19 | } 20 | 21 | def validate_params(self): 22 | # single 23 | validate(self.params, self.get_schema()) 24 | 25 | def exec_main_proc(self): 26 | users_table = self.dynamodb.Table(os.environ['USERS_TABLE_NAME']) 27 | response = users_table.get_item(Key={'user_id': self.params['user_id']}) 28 | 29 | if response.get('Item') is None: 30 | raise RecordNotFoundError('Record Not Found') 31 | 32 | return { 33 | 'statusCode': 200, 34 | 'body': json.dumps(response['Item'], cls=DecimalEncoder) 35 | } 36 | -------------------------------------------------------------------------------- /src/handlers/users/wallet/address/show/handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | from users_wallet_address_show import UsersWalletAddressShow 3 | 4 | cognito = boto3.client('cognito-idp') 5 | 6 | 7 | def lambda_handler(event, context): 8 | users_wallet_address_show = UsersWalletAddressShow(event, context, cognito=cognito) 9 | return users_wallet_address_show.main() 10 | -------------------------------------------------------------------------------- /src/handlers/users/wallet/address/show/users_wallet_address_show.py: -------------------------------------------------------------------------------- 1 | import json 2 | import settings 3 | from user_util import UserUtil 4 | from lambda_base import LambdaBase 5 | from jsonschema import validate 6 | 7 | 8 | class UsersWalletAddressShow(LambdaBase): 9 | def get_schema(self): 10 | return { 11 | 'type': 'object', 12 | 'properties': { 13 | 'user_id': settings.parameters['user_id'] 14 | }, 15 | 'required': ['user_id'] 16 | } 17 | 18 | def validate_params(self): 19 | validate(self.params, self.get_schema()) 20 | 21 | def exec_main_proc(self): 22 | # get private_eth_address 23 | private_eth_address = UserUtil.get_private_eth_address(self.cognito, self.params['user_id']) 24 | 25 | return { 26 | 'statusCode': 200, 27 | 'body': json.dumps({'wallet_address': private_eth_address}) 28 | } 29 | -------------------------------------------------------------------------------- /src/handlers/wallet/bridge_information/show/handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from wallet_bridge_information_show import WalletBridgeInformationShow 3 | 4 | 5 | def lambda_handler(event, context): 6 | wallet_bridge_information_show = WalletBridgeInformationShow(event, context) 7 | return wallet_bridge_information_show.main() 8 | -------------------------------------------------------------------------------- /src/handlers/wallet/bridge_information/show/wallet_bridge_information_show.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import json 4 | from lambda_base import LambdaBase 5 | from decimal_encoder import DecimalEncoder 6 | from private_chain_util import PrivateChainUtil 7 | 8 | 9 | class WalletBridgeInformationShow(LambdaBase): 10 | def get_schema(self): 11 | pass 12 | 13 | def validate_params(self): 14 | pass 15 | 16 | def exec_main_proc(self): 17 | max_single_relay_amount = self.__get_max_single_relay_amount() 18 | min_single_relay_amount = self.__get_min_single_relay_amount() 19 | relay_fee = self.__get_relay_fee() 20 | relay_paused = self.__get_relay_paused() 21 | 22 | result = { 23 | 'max_single_relay_amount': max_single_relay_amount, 24 | 'min_single_relay_amount': min_single_relay_amount, 25 | 'relay_fee': relay_fee, 26 | 'relay_paused': relay_paused, 27 | } 28 | 29 | return { 30 | 'statusCode': 200, 31 | 'body': json.dumps(result, cls=DecimalEncoder) 32 | } 33 | 34 | @staticmethod 35 | def __get_max_single_relay_amount(): 36 | return PrivateChainUtil.send_transaction( 37 | 'https://' + os.environ['PRIVATE_CHAIN_EXECUTE_API_HOST'] + '/production/wallet/max_single_relay_amount') 38 | 39 | @staticmethod 40 | def __get_min_single_relay_amount(): 41 | return PrivateChainUtil.send_transaction( 42 | 'https://' + os.environ['PRIVATE_CHAIN_EXECUTE_API_HOST'] + '/production/wallet/min_single_relay_amount') 43 | 44 | @staticmethod 45 | def __get_relay_fee(): 46 | return PrivateChainUtil.send_transaction( 47 | 'https://' + os.environ['PRIVATE_CHAIN_EXECUTE_API_HOST'] + '/production/wallet/relay_fee') 48 | 49 | @staticmethod 50 | def __get_relay_paused(): 51 | return PrivateChainUtil.send_transaction( 52 | 'https://' + os.environ['PRIVATE_CHAIN_EXECUTE_API_HOST'] + '/production/wallet/relay_paused') 53 | -------------------------------------------------------------------------------- /tests/common/test_cognito_trigger_base.py: -------------------------------------------------------------------------------- 1 | from tests_util import TestsUtil 2 | from unittest import TestCase 3 | from unittest.mock import MagicMock 4 | from jsonschema import ValidationError 5 | from cognito_trigger_base import CognitoTriggerBase 6 | 7 | 8 | class TestCognitoTriggerBase(TestCase): 9 | dynamodb = TestsUtil.get_dynamodb_client() 10 | 11 | class TestLambdaImpl(CognitoTriggerBase): 12 | def get_schema(self): 13 | pass 14 | 15 | def validate_params(self): 16 | pass 17 | 18 | def exec_main_proc(self): 19 | pass 20 | 21 | def test_catch_validation_error(self): 22 | lambda_impl = self.TestLambdaImpl({}, {}, self.dynamodb) 23 | lambda_impl.exec_main_proc = MagicMock(side_effect=ValidationError('not valid')) 24 | with self.assertRaises(Exception) as e: 25 | lambda_impl.main() 26 | self.assertEqual('not valid', str(e.exception)) 27 | 28 | def test_catch_exception_error(self): 29 | lambda_impl = self.TestLambdaImpl({}, {}, self.dynamodb) 30 | lambda_impl.exec_main_proc = MagicMock(side_effect=Exception('exception')) 31 | with self.assertRaises(Exception) as e: 32 | lambda_impl.main() 33 | self.assertEqual('Internal server error', str(e.exception)) 34 | -------------------------------------------------------------------------------- /tests/common/test_crypto_util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import boto3 3 | import settings 4 | import base64 5 | from user_util import UserUtil 6 | from crypto_util import CryptoUtil 7 | from unittest import TestCase 8 | from tests_util import TestsUtil 9 | 10 | 11 | class TestCryptoUtil(TestCase): 12 | def setUp(self): 13 | self.cognito = boto3.client('cognito-idp') 14 | self.dynamodb = TestsUtil.get_dynamodb_client() 15 | os.environ['COGNITO_USER_POOL_ID'] = 'cognito_user_pool' 16 | os.environ['LOGIN_SALT'] = '4YGjw4llWxC46bNluUYu1bhaWQgfJjB4' 17 | TestsUtil.set_all_tables_name_to_env() 18 | TestsUtil.delete_all_tables(self.dynamodb) 19 | 20 | self.external_provider_users_table_items = [ 21 | { 22 | 'external_provider_user_id': 'external_provider_user_id' 23 | } 24 | ] 25 | TestsUtil.create_table( 26 | self.dynamodb, 27 | os.environ['EXTERNAL_PROVIDER_USERS_TABLE_NAME'], 28 | self.external_provider_users_table_items 29 | ) 30 | TestsUtil.create_table(self.dynamodb, os.environ['USERS_TABLE_NAME'], []) 31 | 32 | def test_get_external_provider_password_ok(self): 33 | aes_iv = os.urandom(settings.AES_IV_BYTES) 34 | encrypted_password = CryptoUtil.encrypt_password('nNU8E9E6OSe9tRQn', aes_iv) 35 | iv = base64.b64encode(aes_iv).decode() 36 | 37 | UserUtil.add_external_provider_user_info( 38 | dynamodb=self.dynamodb, 39 | external_provider_user_id='user_id', 40 | password=encrypted_password, 41 | iv=iv, 42 | email='email' 43 | ) 44 | 45 | password = CryptoUtil.get_external_provider_password( 46 | self.dynamodb, 47 | 'user_id' 48 | ) 49 | self.assertEqual(password, 'nNU8E9E6OSe9tRQn') 50 | -------------------------------------------------------------------------------- /tests/common/test_es_util.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from elasticsearch import Elasticsearch 4 | from tests_es_util import TestsEsUtil 5 | 6 | from es_util import ESUtil 7 | 8 | 9 | class TestDBUtil(TestCase): 10 | elasticsearch = Elasticsearch( 11 | hosts=[{'host': 'localhost'}] 12 | ) 13 | 14 | def setUp(self): 15 | TestsEsUtil.create_tag_index(self.elasticsearch) 16 | 17 | def tearDown(self): 18 | self.elasticsearch.indices.delete(index="tags", ignore=[404]) 19 | 20 | def test_search_tag(self): 21 | TestsEsUtil.create_tag_with_count(self.elasticsearch, 'ALIS', 5) 22 | TestsEsUtil.create_tag_with_count(self.elasticsearch, 'apple orange', 2) 23 | TestsEsUtil.create_tag_with_count(self.elasticsearch, 'beautiful Apple', 3) 24 | TestsEsUtil.create_tag_with_count(self.elasticsearch, '漢字 カタカナ', 1) 25 | TestsEsUtil.create_tag_with_count(self.elasticsearch, 'Application', 4) 26 | 27 | self.elasticsearch.indices.refresh(index="tags") 28 | 29 | self.__assert_search_tags('A', ['ALIS', 'Application', 'apple orange']) 30 | self.__assert_search_tags('漢', ['漢字 カタカナ']) 31 | self.__assert_search_tags('app', ['Application', 'apple orange']) 32 | self.__assert_search_tags('カタ', []) 33 | self.__assert_search_tags('apple ora', ['apple orange']) 34 | self.__assert_search_tags('ALIS on', []) 35 | 36 | def test_search_tag_with_limit(self): 37 | # 0~10のループを回す 38 | for x in range(0, 11): 39 | TestsEsUtil.create_tag_with_count(self.elasticsearch, 'A' + str(x), x) 40 | 41 | result = ESUtil.search_tag(self.elasticsearch, 'A', 5, 1) 42 | self.assertEquals(len(result), 5) 43 | 44 | result = ESUtil.search_tag(self.elasticsearch, 'A', 2, 2) 45 | self.assertEquals(len(result), 2) 46 | self.assertEquals([tag['name'] for tag in result], ['A8', 'A7']) 47 | 48 | def __assert_search_tags(self, word, expected): 49 | result = ESUtil.search_tag(self.elasticsearch, word, 10, 1) 50 | tags = [tag['name'] for tag in result] 51 | self.assertEquals(tags, expected) 52 | -------------------------------------------------------------------------------- /tests/common/test_time_util.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from time_util import TimeUtil 3 | 4 | 5 | class TestTimeUtil(TestCase): 6 | def test_generate_sort_key(self): 7 | sort_key = TimeUtil.generate_sort_key() 8 | 9 | self.assertEqual(len(str(sort_key)), 16) 10 | self.assertTrue(type(sort_key) is int) 11 | -------------------------------------------------------------------------------- /tests/handlers/login/line/authorize_url/test_login_line_authorize_url.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import settings 4 | from unittest import TestCase 5 | from unittest.mock import patch, MagicMock 6 | from login_line_authorize_url import LoginLineAuthorizeUrl 7 | 8 | 9 | class TestUserUtil(TestCase): 10 | def setUp(self): 11 | os.environ['LINE_CHANNEL_ID'] = 'aaaaaaaaaaa' 12 | os.environ['LINE_REDIRECT_URI'] = 'https://xxxxxxx.com' 13 | 14 | @patch("login_line_authorize_url.LoginLineAuthorizeUrl._LoginLineAuthorizeUrl__generate_state", 15 | MagicMock(return_value='r8yu78j9s')) 16 | def test_main_ok(self): 17 | url = settings.LINE_AUTHORIZE_URL + os.environ['LINE_CHANNEL_ID'] + '&redirect_uri=' +\ 18 | os.environ['LINE_REDIRECT_URI'] + '&state=r8yu78j9s' + settings.LINE_LOGIN_REQUEST_SCOPE 19 | response = LoginLineAuthorizeUrl(event={}, context="").main() 20 | self.assertEqual(response['statusCode'], 200) 21 | self.assertEqual( 22 | json.loads(response['body']), 23 | {'callback_url': url} 24 | ) 25 | -------------------------------------------------------------------------------- /tests/handlers/login/twitter/authorization_url/test_login_twitter_authorization_url.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | from botocore.exceptions import ClientError 4 | from unittest import TestCase 5 | from unittest.mock import patch 6 | from login_twitter_authorization_url import LoginTwitterAuthorizationUrl 7 | 8 | 9 | class TestLoginTwitterAuthorizationUrl(TestCase): 10 | @classmethod 11 | def setUpClass(cls): 12 | os.environ['TWITTER_CONSUMER_KEY'] = 'fake_twitter_consumer_key' 13 | os.environ['TWITTER_CONSUMER_SECRET'] = 'fake_twitter_consumer_secret' 14 | os.environ['TWITTER_OAUTH_CALLBACK_URL'] = 'http://localhost' 15 | 16 | def test_exec_main_ok(self): 17 | with patch('login_twitter_authorization_url.TwitterUtil') as twitter_mock: 18 | twitter_mock.return_value.generate_auth_url.return_value = 'oauth_url' 19 | response = LoginTwitterAuthorizationUrl({}, {}).main() 20 | self.assertEqual(response['statusCode'], 200) 21 | self.assertEqual( 22 | json.loads(response['body']), 23 | {'url': 'oauth_url'} 24 | ) 25 | 26 | def test_exec_main_ng(self): 27 | with patch('login_twitter_authorization_url.TwitterUtil') as twitter_mock: 28 | twitter_mock.return_value.generate_auth_url.side_effect = ClientError( 29 | {'Error': {'Code': 'UserNotFoundException'}}, 30 | 'operation_name' 31 | ) 32 | response = LoginTwitterAuthorizationUrl({}, {}).main() 33 | self.assertEqual(response['statusCode'], 500) 34 | self.assertEqual( 35 | json.loads(response['body']), 36 | {'message': 'Internal server error: LoginTwitterAuthorizationUrl'} 37 | ) 38 | -------------------------------------------------------------------------------- /tests/handlers/me/allowed_applications/delete/test_me_allowed_applications_delete.py: -------------------------------------------------------------------------------- 1 | import os 2 | from unittest import TestCase 3 | from unittest.mock import patch, MagicMock 4 | 5 | import requests 6 | import responses 7 | 8 | import settings 9 | from me_allowed_applications_delete import MeAllowedApplicationsDelete 10 | 11 | 12 | class TestMeAllowedApplicationsDelete(TestCase): 13 | def setUp(self): 14 | os.environ['AUTHLETE_API_KEY'] = 'XXXXXXXXXXXXXXXXX' 15 | os.environ['AUTHLETE_API_SECRET'] = 'YYYYYYYYYYYYYY' 16 | 17 | def tearDown(self): 18 | pass 19 | 20 | @responses.activate 21 | def test_main_ok(self): 22 | params = { 23 | 'requestContext': { 24 | 'authorizer': { 25 | 'claims': { 26 | 'cognito:username': 'user01' 27 | } 28 | } 29 | }, 30 | 'body': '{"client_id":12345}' 31 | } 32 | responses.add(responses.DELETE, settings.AUTHLETE_CLIENT_ENDPOINT + '/authorization/delete/12345/user01', 33 | json={'statusCode': 200, 'body': ''}, status=200) 34 | response = MeAllowedApplicationsDelete(params, {}).main() 35 | self.assertEqual(response['statusCode'], 200) 36 | 37 | @patch('requests.get', MagicMock(side_effect=requests.exceptions.RequestException())) 38 | def test_main_with_exception(self): 39 | params = { 40 | 'requestContext': { 41 | 'authorizer': { 42 | 'claims': { 43 | 'cognito:username': 'user01' 44 | } 45 | } 46 | }, 47 | 'body': '{"client_id":12345}' 48 | } 49 | response = MeAllowedApplicationsDelete(params, {}).main() 50 | self.assertEqual(response['statusCode'], 500) 51 | -------------------------------------------------------------------------------- /tests/handlers/me/applications/index/test_me_applications_index.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from unittest import TestCase 4 | from unittest.mock import patch, MagicMock 5 | 6 | import requests 7 | import responses 8 | 9 | import settings 10 | from me_applications_index import MeApplicationIndex 11 | 12 | 13 | class TestMeApplicationsIndex(TestCase): 14 | def setUp(self): 15 | os.environ['AUTHLETE_API_KEY'] = 'XXXXXXXXXXXXXXXXX' 16 | os.environ['AUTHLETE_API_SECRET'] = 'YYYYYYYYYYYYYY' 17 | 18 | def tearDown(self): 19 | pass 20 | 21 | @responses.activate 22 | def test_main_ok(self): 23 | params = { 24 | 'requestContext': { 25 | 'authorizer': { 26 | 'claims': { 27 | 'cognito:username': 'user01' 28 | } 29 | } 30 | } 31 | } 32 | 33 | responses.add(responses.GET, settings.AUTHLETE_CLIENT_ENDPOINT + '/get/list', 34 | json={"developer": "matsumatsu20"}, status=200) 35 | 36 | response = MeApplicationIndex(params, {}).main() 37 | 38 | self.assertEqual(response['statusCode'], 200) 39 | self.assertEqual(json.loads(response['body']), {"developer": "matsumatsu20"}) 40 | 41 | @patch('requests.get', MagicMock(side_effect=requests.exceptions.RequestException())) 42 | def test_main_with_exception(self): 43 | params = { 44 | 'requestContext': { 45 | 'authorizer': { 46 | 'claims': { 47 | 'cognito:username': 'user01' 48 | } 49 | } 50 | } 51 | } 52 | 53 | response = MeApplicationIndex(params, {}).main() 54 | self.assertEqual(response['statusCode'], 500) 55 | -------------------------------------------------------------------------------- /tests/handlers/me/wallet/allowance/show/test_me_wallet_allowance_show.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | from unittest import TestCase 4 | from me_wallet_allowance_show import MeWalletAllowanceShow 5 | from unittest.mock import patch, MagicMock 6 | 7 | 8 | class TestMeWalletAllowanceShow(TestCase): 9 | dynamodb = boto3.resource('dynamodb', endpoint_url='http://localhost:4569/') 10 | 11 | def test_main_ok(self): 12 | test_address = '0x5d7743a4a6f21593ff6d3d81595f270123456789' 13 | params = { 14 | 'requestContext': { 15 | 'authorizer': { 16 | 'claims': { 17 | 'cognito:username': 'test-user', 18 | 'custom:private_eth_address': test_address 19 | } 20 | } 21 | } 22 | } 23 | test_allowance = '0x10' 24 | magic_lib = MagicMock(return_value=test_allowance) 25 | with patch('private_chain_util.PrivateChainUtil.get_allowance', magic_lib): 26 | response = MeWalletAllowanceShow(params, {}, dynamodb=self.dynamodb).main() 27 | self.assertEqual(200, response['statusCode']) 28 | self.assertEqual({'allowance': test_allowance}, json.loads(response['body'])) 29 | 30 | args, _ = magic_lib.call_args 31 | self.assertEqual(test_address, args[0]) 32 | 33 | def test_main_ok_not_exists_private_eth_address(self): 34 | params = { 35 | 'requestContext': { 36 | 'authorizer': { 37 | 'claims': { 38 | 'cognito:username': 'test-user' 39 | } 40 | } 41 | } 42 | } 43 | response = MeWalletAllowanceShow(params, {}, dynamodb=self.dynamodb).main() 44 | self.assertEqual(200, response['statusCode']) 45 | self.assertEqual({'allowance': '0x0'}, json.loads(response['body'])) 46 | -------------------------------------------------------------------------------- /tests/handlers/me/wallet/balance/README.md: -------------------------------------------------------------------------------- 1 | ※Cognito + プライベートチェーンへのリクエストなのでLocalStack使えず。テスト割愛 2 | -------------------------------------------------------------------------------- /tests/handlers/me/wallet/nonce/show/test_me_wallet_nonce_show.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | from unittest import TestCase 4 | from me_wallet_nonce_show import MeWalletNonceShow 5 | from unittest.mock import patch, MagicMock 6 | 7 | 8 | class TestMeWalletNonceShow(TestCase): 9 | dynamodb = boto3.resource('dynamodb', endpoint_url='http://localhost:4569/') 10 | 11 | def test_main_ok(self): 12 | test_address = '0x5d7743a4a6f21593ff6d3d81595f270123456789' 13 | params = { 14 | 'requestContext': { 15 | 'authorizer': { 16 | 'claims': { 17 | 'cognito:username': 'test-user', 18 | 'custom:private_eth_address': test_address 19 | } 20 | } 21 | } 22 | } 23 | test_nonce = '0x10' 24 | magic_lib = MagicMock(return_value=test_nonce) 25 | with patch('private_chain_util.PrivateChainUtil.get_transaction_count', magic_lib): 26 | response = MeWalletNonceShow(params, {}, dynamodb=self.dynamodb).main() 27 | self.assertEqual(200, response['statusCode']) 28 | self.assertEqual({'nonce': test_nonce}, json.loads(response['body'])) 29 | 30 | args, _ = magic_lib.call_args 31 | self.assertEqual(test_address, args[0]) 32 | 33 | def test_main_ok_not_exists_private_eth_address(self): 34 | params = { 35 | 'requestContext': { 36 | 'authorizer': { 37 | 'claims': { 38 | 'cognito:username': 'test-user' 39 | } 40 | } 41 | } 42 | } 43 | response = MeWalletNonceShow(params, {}, dynamodb=self.dynamodb).main() 44 | self.assertEqual(200, response['statusCode']) 45 | self.assertEqual({'nonce': '0x0'}, json.loads(response['body'])) 46 | -------------------------------------------------------------------------------- /tests/handlers/me/wallet/tip/README.md: -------------------------------------------------------------------------------- 1 | ※Cognito + プライベートチェーンへのリクエストなのでLocalStack使えず。テスト割愛 2 | -------------------------------------------------------------------------------- /tests/handlers/sign_up/line/authorize_url/test_sign_up_line_authorize_url.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import settings 4 | from unittest import TestCase 5 | from unittest.mock import patch, MagicMock 6 | from sign_up_line_authorize_url import SignUpLineAuthorizeUrl 7 | 8 | 9 | class TestSignUpLineAuthorizeUrl(TestCase): 10 | def setUp(self): 11 | os.environ['LINE_CHANNEL_ID'] = 'aaaaaaaaaaa' 12 | os.environ['LINE_REDIRECT_URI'] = 'https://xxxxxxx.com' 13 | 14 | @patch("sign_up_line_authorize_url.SignUpLineAuthorizeUrl._SignUpLineAuthorizeUrl__generate_state", 15 | MagicMock(return_value='r8yu78j9s')) 16 | def test_main_ok(self): 17 | url = settings.LINE_AUTHORIZE_URL + os.environ['LINE_CHANNEL_ID'] + '&redirect_uri=' +\ 18 | os.environ['LINE_REDIRECT_URI'] + '&state=r8yu78j9s' + settings.LINE_REQUEST_SCOPE 19 | response = SignUpLineAuthorizeUrl(event={}, context="").main() 20 | self.assertEqual(response['statusCode'], 200) 21 | self.assertEqual( 22 | json.loads(response['body']), 23 | {'callback_url': url} 24 | ) 25 | -------------------------------------------------------------------------------- /tests/handlers/topics/index/test_topics_index.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from unittest import TestCase 4 | 5 | import settings 6 | 7 | from topics_index import TopicsIndex 8 | from tests_util import TestsUtil 9 | 10 | 11 | class TestTopicsIndex(TestCase): 12 | dynamodb = TestsUtil.get_dynamodb_client() 13 | 14 | @classmethod 15 | def setUpClass(cls): 16 | TestsUtil.set_all_tables_name_to_env() 17 | TestsUtil.delete_all_tables(cls.dynamodb) 18 | 19 | # create users_table 20 | cls.topic_items = [ 21 | {'name': 'crypto', 'display_name': '暗号通貨', 'order': 1, 'index_hash_key': settings.TOPIC_INDEX_HASH_KEY}, 22 | {'name': 'fashion', 'fashion': 'ファッション', 'order': 2, 'index_hash_key': settings.TOPIC_INDEX_HASH_KEY}, 23 | {'name': 'food', 'fashion': '食', 'order': 3, 'index_hash_key': settings.TOPIC_INDEX_HASH_KEY} 24 | ] 25 | TestsUtil.create_table(cls.dynamodb, os.environ['TOPIC_TABLE_NAME'], cls.topic_items) 26 | 27 | @classmethod 28 | def tearDownClass(cls): 29 | TestsUtil.delete_all_tables(cls.dynamodb) 30 | 31 | def test_main(self): 32 | response = TopicsIndex({}, {}, dynamodb=self.dynamodb).main() 33 | 34 | self.assertEqual(response['statusCode'], 200) 35 | self.assertEqual(json.loads(response['body']), self.topic_items) 36 | -------------------------------------------------------------------------------- /tests/handlers/users/wallet/address/show/test_users_wallet_address_show.py: -------------------------------------------------------------------------------- 1 | import os 2 | import boto3 3 | import json 4 | from unittest import TestCase 5 | from unittest.mock import MagicMock 6 | from users_wallet_address_show import UsersWalletAddressShow 7 | 8 | 9 | class TestUsersWalletAddressShow(TestCase): 10 | cognito = boto3.client('cognito-idp') 11 | test_user_id = 'test-user' 12 | 13 | @classmethod 14 | def setUpClass(cls): 15 | os.environ['COGNITO_USER_POOL_ID'] = 'xxxxxxx' 16 | 17 | def assert_bad_request(self, params): 18 | function = UsersWalletAddressShow(params, {}, cognito=self.cognito) 19 | response = function.main() 20 | 21 | self.assertEqual(response['statusCode'], 400) 22 | 23 | def test_main_ok(self): 24 | test_address = '0x401BA17D89D795B3C6e373c5062F1C3F8979e73B' 25 | self.cognito.admin_get_user = MagicMock(return_value={ 26 | 'UserAttributes': [ 27 | { 28 | 'Name': 'hoge', 29 | 'Value': 'piyo' 30 | }, 31 | { 32 | 'Name': 'custom:private_eth_address', 33 | 'Value': test_address 34 | } 35 | ] 36 | }) 37 | params = { 38 | 'pathParameters': { 39 | 'user_id': self.test_user_id 40 | } 41 | } 42 | 43 | response = UsersWalletAddressShow(params, {}, cognito=self.cognito).main() 44 | 45 | self.assertEqual(response['statusCode'], 200) 46 | self.assertEqual(json.loads(response['body']), {'wallet_address': test_address}) 47 | 48 | def test_validation_with_no_user_id_param(self): 49 | params = { 50 | 'pathParameters': { 51 | } 52 | } 53 | 54 | self.assert_bad_request(params) 55 | 56 | def test_validation_user_id_min(self): 57 | params = { 58 | 'pathParameters': { 59 | 'user_id': 'AA' 60 | } 61 | } 62 | 63 | self.assert_bad_request(params) 64 | 65 | def test_validation_user_id_max(self): 66 | params = { 67 | 'pathParameters': { 68 | 'user_id': 'A' * 51 69 | } 70 | } 71 | 72 | self.assert_bad_request(params) 73 | -------------------------------------------------------------------------------- /tests/handlers/wallet/bridge_information/show/test_wallet_bridge_information_show.py: -------------------------------------------------------------------------------- 1 | import json 2 | from unittest import TestCase 3 | from wallet_bridge_information_show import WalletBridgeInformationShow 4 | from unittest.mock import patch, MagicMock 5 | 6 | 7 | class TestWalletBridgeInformationShow(TestCase): 8 | 9 | @patch( 10 | 'wallet_bridge_information_show.WalletBridgeInformationShow._WalletBridgeInformationShow__get_max_single_relay_amount', 11 | MagicMock(return_value='0x0000000000000000000000000000000000000000000000000000000000002710')) 12 | @patch( 13 | 'wallet_bridge_information_show.WalletBridgeInformationShow._WalletBridgeInformationShow__get_min_single_relay_amount', 14 | MagicMock(return_value='0x0000000000000000000000000000000000000000000000000000000000000065')) 15 | @patch('wallet_bridge_information_show.WalletBridgeInformationShow._WalletBridgeInformationShow__get_relay_fee', 16 | MagicMock(return_value='0x0000000000000000000000000000000000000000000000000000000000000064')) 17 | @patch('wallet_bridge_information_show.WalletBridgeInformationShow._WalletBridgeInformationShow__get_relay_paused', 18 | MagicMock(return_value='0x0000000000000000000000000000000000000000000000000000000000000000')) 19 | def test_main_ok(self): 20 | response = WalletBridgeInformationShow({}, {}).main() 21 | 22 | expected = { 23 | 'max_single_relay_amount': '0x0000000000000000000000000000000000000000000000000000000000002710', 24 | 'min_single_relay_amount': '0x0000000000000000000000000000000000000000000000000000000000000065', 25 | 'relay_fee': '0x0000000000000000000000000000000000000000000000000000000000000064', 26 | 'relay_paused': '0x0000000000000000000000000000000000000000000000000000000000000000' 27 | } 28 | 29 | self.assertEqual(json.loads(response['body']), expected) 30 | --------------------------------------------------------------------------------