diff --git a/.github/workflows/develop-pipeline.yml b/.github/workflows/develop-pipeline.yml index 02f534378..f4b1fbb1c 100644 --- a/.github/workflows/develop-pipeline.yml +++ b/.github/workflows/develop-pipeline.yml @@ -75,7 +75,6 @@ jobs: SOLANA_DEVNET_NODE_RPC_URL: ${{ secrets.SOLANA_DEVNET_NODE_RPC_URL }} SOLANA_MAINNET_NODE_RPC_URL: ${{ secrets.SOLANA_MAINNET_NODE_RPC_URL }} MPETH_GRAPHQL_PRICES_URL: ${{ secrets.MPETH_GRAPHQL_PRICES_URL }} - GIV_POWER_SUBGRAPH_URL: ${{ secrets.GIV_POWER_SUBGRAPH_URL }} publish: needs: test diff --git a/.github/workflows/master-pipeline.yml b/.github/workflows/master-pipeline.yml index e069f6c16..14e0b2483 100644 --- a/.github/workflows/master-pipeline.yml +++ b/.github/workflows/master-pipeline.yml @@ -113,7 +113,6 @@ jobs: SOLANA_DEVNET_NODE_RPC_URL: ${{ secrets.SOLANA_DEVNET_NODE_RPC_URL }} SOLANA_MAINNET_NODE_RPC_URL: ${{ secrets.SOLANA_MAINNET_NODE_RPC_URL }} MPETH_GRAPHQL_PRICES_URL: ${{ secrets.MPETH_GRAPHQL_PRICES_URL }} - GIV_POWER_SUBGRAPH_URL: ${{ secrets.GIV_POWER_SUBGRAPH_URL }} publish: needs: test diff --git a/.github/workflows/staging-pipeline.yml b/.github/workflows/staging-pipeline.yml index c35112702..b4d3111c0 100644 --- a/.github/workflows/staging-pipeline.yml +++ b/.github/workflows/staging-pipeline.yml @@ -138,7 +138,6 @@ jobs: SOLANA_DEVNET_NODE_RPC_URL: ${{ secrets.SOLANA_DEVNET_NODE_RPC_URL }} SOLANA_MAINNET_NODE_RPC_URL: ${{ secrets.SOLANA_MAINNET_NODE_RPC_URL }} MPETH_GRAPHQL_PRICES_URL: ${{ secrets.MPETH_GRAPHQL_PRICES_URL }} - GIV_POWER_SUBGRAPH_URL: ${{ secrets.GIV_POWER_SUBGRAPH_URL }} publish: needs: test diff --git a/README.md b/README.md index 2406f7a5e..12769f914 100644 --- a/README.md +++ b/README.md @@ -71,10 +71,6 @@ You can see logs beautifully with this command ``` -## Features - -- [Power Boosting Specs](./docs/powerBoosting.md) - ### Authentication ### Start fast @@ -246,20 +242,6 @@ in below image links [![](https://mermaid.ink/img/pako:eNrFVcFu2zAM_RXCl7VAmgA9-hCga9euQJcNS7tTgEGVaEeNLXkSnSwo-u-jJDtJ03TYgA7zyYbI98j3aOoxk1Zhlmcef7RoJF5oUTpRzwzwIyRZB3ceXfpuhCMtdSMMwaWzhtColyfvhVwcPJhOQHiYWqlFBROklXULCGEzk4J7TDgZj3uYHM5FVYFQaoKrlPvF2UJXCHVLgrQ1cOQ5UJsSfDzvoQUjNc4-oKRv6HShZQy_tK6-VseJ0lhCsEt0W74pEmjTtARKkOBXcKi0j3AlGnSCUwSQ4wQmPWcBX8X6JEIItK6C-zXISqOhazWIiI7ruuODAQyHw4TQ5cFJEKBXI4evDql1JsAcEGo64YgOD1o2C8h2SoDppVDKofcpm32IBMHYHM78YpPlUCiQ1rCeFPutbKk7c0I0bAhvwsFLom5uKoIL7aV13RT0rLu2vih55Hfd9SPJvt9z9EglKFhpmvNMSm7kO1lGCd7w2Lo1eHKs85ZsI-R4HKq9Yktj_doUNjiRUG4DyPMK90pMsveZKXQz26HNK2vLCt-iyzIiDVKX4a_8o-6iGdPwEVOYoORe_6q9neDouVZJ3W3mgcG-wFRiFwtHoOmdh4cVHXc_Smr5OgheOFsDzdHhjoJbDW-0WbBM5i1UrDqs_6bjvnb7HHGbzYkan49GotHDvuKhtPVoeTqq8TdUYdG9Oo8HbLqKu6y3IO2xKMzu9oqG0dzZFeBPiU3cqbpIjjIoLEWlX-c4560RN-IyLllU8MyciO6wCjGagrIHdjIUvJS3E_H57O7242naPuDbKOxhSbdLsuh3YtmuPfi5bSt-10tkvbi4IHNg4UZZg6Z1jfW4J-AefSG4fPWPebNBVqOrhVZ8Cz_G2zDjX4WnIMv5VWEh2opm2cw8cWjb8J2EH5TmiznLC1F5HGSiJTtdG5nl5Frsg7qbvIt6-gW6nqHe)](https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNrFVcFu2zAM_RXCl7VAmgA9-hCga9euQJcNS7tTgEGVaEeNLXkSnSwo-u-jJDtJ03TYgA7zyYbI98j3aOoxk1Zhlmcef7RoJF5oUTpRzwzwIyRZB3ceXfpuhCMtdSMMwaWzhtColyfvhVwcPJhOQHiYWqlFBROklXULCGEzk4J7TDgZj3uYHM5FVYFQaoKrlPvF2UJXCHVLgrQ1cOQ5UJsSfDzvoQUjNc4-oKRv6HShZQy_tK6-VseJ0lhCsEt0W74pEmjTtARKkOBXcKi0j3AlGnSCUwSQ4wQmPWcBX8X6JEIItK6C-zXISqOhazWIiI7ruuODAQyHw4TQ5cFJEKBXI4evDql1JsAcEGo64YgOD1o2C8h2SoDppVDKofcpm32IBMHYHM78YpPlUCiQ1rCeFPutbKk7c0I0bAhvwsFLom5uKoIL7aV13RT0rLu2vih55Hfd9SPJvt9z9EglKFhpmvNMSm7kO1lGCd7w2Lo1eHKs85ZsI-R4HKq9Yktj_doUNjiRUG4DyPMK90pMsveZKXQz26HNK2vLCt-iyzIiDVKX4a_8o-6iGdPwEVOYoORe_6q9neDouVZJ3W3mgcG-wFRiFwtHoOmdh4cVHXc_Smr5OgheOFsDzdHhjoJbDW-0WbBM5i1UrDqs_6bjvnb7HHGbzYkan49GotHDvuKhtPVoeTqq8TdUYdG9Oo8HbLqKu6y3IO2xKMzu9oqG0dzZFeBPiU3cqbpIjjIoLEWlX-c4560RN-IyLllU8MyciO6wCjGagrIHdjIUvJS3E_H57O7242naPuDbKOxhSbdLsuh3YtmuPfi5bSt-10tkvbi4IHNg4UZZg6Z1jfW4J-AefSG4fPWPebNBVqOrhVZ8Cz_G2zDjX4WnIMv5VWEh2opm2cw8cWjb8J2EH5TmiznLC1F5HGSiJTtdG5nl5Frsg7qbvIt6-gW6nqHe) -### Power Snapshot - -Impact graph supports ranking projects based on power boosted by users. -Users who have GIVpower, can boost a project by allocating a portion (percentage) of their GIVpower to that project and after that impact-graph regularly takes snapshot of user GIVpower balance and boost percentages. -At the end of each givback round (14 days), average of allocated power will be the effective power balance of each project. - -Snapshotting mechanism is implemented in by the hlp of database cron job and impact graph support of historic user balance on blockchain. - -##### Database Snapshot - -Snapshot taking on database is implemented by the help `pg_cron` extension on Postgres database. -On regular interval (defined by cron job expression), calls a db procedure called public."TAKE_POWER_BOOSTING_SNAPSHOT". -This procedure creates a new record of power_snapshot and copies power boosting percentages content to another table and associates them to the new power_snapshot record. - ###### Cron Job Creation Cron job creation for test environment is already implemented in dbCronRepository.ts and a modified docker with enabled `pg_cron` extension. @@ -303,8 +285,3 @@ SELECT CRON.schedule( '*/5 * * * *', $$CALL public."ARCHIVE_POWER_BOOSTING_OLD_SNAPSHOT_DATA"()$$); ``` - -##### User GIVpower balance snapshot - -impact-graph monitors power_snapshot table and whenever a new record is created it find corresponding ethereum blockchain block number and fills in the snapshot record. -Then for every user who has a percentage snapshot, fills balance snapshot table with the user balance at the corresponding block number by the help of impact graph block filter. diff --git a/config/example.env b/config/example.env index ad5426562..15b219cf6 100644 --- a/config/example.env +++ b/config/example.env @@ -115,21 +115,6 @@ TWITTER_CLIENT_ID= TWITTER_CLIENT_SECRET= TWITTER_CALLBACK_URL=https://dev.serve.giveth.io/socialProfiles/callback/twitter -GIVPOWER_BOOSTING_USER_PROJECTS_LIMIT=5 -GIVPOWER_BOOSTING_PERCENTAGE_PRECISION=2 - -#GIV_POWER_SUBGRAPH_ADAPTER=givPower -GIV_POWER_SUBGRAPH_ADAPTER=mock -GIV_POWER_SUBGRAPH_URL=https://api.thegraph.com/subgraphs/name/aminlatifi/giveconomy-xdai-deployment-seven -GIV_POWER_UNIPOOL_CONTRACT_ID=0xdaea66adc97833781139373df5b3bced3fdda5b1 -FIRST_GIVBACK_ROUND_TIME_STAMP=1640361600 -# GIVpower round 14days * 24 hours * 3600seconds =1209600 -GIVPOWER_ROUND_DURATION=1209600 -FILL_POWER_SNAPSHOT_BALANCE_SERVICE_ACTIVE=true -FILL_POWER_SNAPSHOT_BALANCE_CRONJOB_EXPRESSION=0 0 * * * * -NUMBER_OF_FILLING_POWER_SNAPSHOT_BALANCE_CONCURRENT_JOB=5 - - #NOTIFICATION_CENTER_ADAPTER=notificationCenter NOTIFICATION_CENTER_ADAPTER=mock NOTIFICATION_CENTER_BASE_URL= @@ -144,20 +129,9 @@ PROJECT_UPDATES_EXPIRED_ADDITIONAL_REVOKE_DAYS=30 PROJECT_REVOKE_SERVICE_ACTIVE=true PROJECT_UPDATES_FIRST_REVOKE_BATCH_DATE=2022-10-22 -FILL_POWER_SNAPSHOT_SERVICE_ACTIVE=true - -#Every 30 minutes -UPDATE_POWER_ROUND_CRONJOB_EXPRESSION=10 */30 * * * * -UPDATE_POWER_SNAPSHOT_SERVICE_ACTIVE=true - GIVBACK_MIN_FACTOR=0.5 GIVBACK_MAX_FACTOR=0.8 -ENABLE_DB_POWER_BOOSTING_SNAPSHOT= - -DB_POWER_BOOSTING_SNAPSHOT_CRONJOB_EXPRESSION= -ARCHIVE_POWER_BOOSTING_OLD_SNAPSHOT_DATA_CRONJOB_EXPRESSION= - PROJECT_FILTERS_THREADS_POOL_CONCURRENCY=1 PROJECT_FILTERS_THREADS_POOL_NAME=ProjectFiltersThreadPool PROJECT_FILTERS_THREADS_POOL_SIZE=4 @@ -181,12 +155,6 @@ SERVICE_NAME=example OPTIMISM_NODE_HTTP_URL=https://optimism-mainnet.public.blastapi.io/ OPTIMISM_SEPOLIA_NODE_HTTP_URL= -####################################### INSTANT BOOSTING ################################# -# OPTIONAL - default: false -ENABLE_INSTANT_BOOSTING_UPDATE=true -# OPTIONAL - default: Every 5 minutes -INSTANT_BOOSTING_UPDATE_CRONJOB_EXPRESSION=0 */5 * * * * - # Gitcoin API GITCOIN_ADAPTER=mock GITCOIN_PASSPORT_API= @@ -195,11 +163,6 @@ GITCOIN_SCORER_ID= # OPTIONAL - default: Every 10 minutes CHECK_QF_ROUND_ACTIVE_STATUS_CRONJOB_EXPRESSION=*/10 * * * * -BALANCE_AGGREGATOR_BASE_URL=https://dev.serve.giveth.io/givpower-balance-aggregator -POWER_BALANCE_AGGREGATOR_ADAPTER=powerBalanceAggregator -#POWER_BALANCE_AGGREGATOR_ADAPTER=mock -NUMBER_OF_BALANCE_AGGREGATOR_BATCH=20 - # OPTIONAL - default: 60000 (1 minute QF_ROUND_ESTIMATED_MATCHING_CACHE_DURATION=60000 diff --git a/config/test.env b/config/test.env index d67d2171d..13956e192 100644 --- a/config/test.env +++ b/config/test.env @@ -114,24 +114,6 @@ LINKEDIN_CLIENT_ID= LINKEDIN_CLIENT_SECRET= LINKEDIN_REDIRECT_URL=http://localhost:3040/socialProfiles/callback/linkedin -GIVPOWER_BOOSTING_USER_PROJECTS_LIMIT=5 -GIVPOWER_BOOSTING_PERCENTAGE_PRECISION=2 - -# GIVpower round 14days * 24 hours * 3600seconds =1209600 -GIVPOWER_ROUND_DURATION=1209600 - -GIV_POWER_SUBGRAPH_ADAPTER=mock -FIRST_GIVBACK_ROUND_TIME_STAMP=1640361600 -GIV_POWER_SUBGRAPH_URL= -_GIV_POWER_SUBGRAPH_URL=http://localhost:8000/subgraphs/name/local/staging -GIV_POWER_UNIPOOL_CONTRACT_ID=0xdaea66adc97833781139373df5b3bced3fdda5b1 -FILL_POWER_SNAPSHOT_BALANCE_SERVICE_ACTIVE=false -FILL_POWER_SNAPSHOT_BALANCE_CRONJOB_EXPRESSION=0 * * * * * -# Set 10 to do it fast while running tests -NUMBER_OF_FILLING_POWER_SNAPSHOT_BALANCE_CONCURRENT_JOB=20 -ENABLE_DB_POWER_BOOSTING_SNAPSHOT=false -DB_POWER_BOOSTING_SNAPSHOT_CRONJOB_EXPRESSION =0 0 1 1 * - NOTIFICATION_CENTER_ADAPTER=mock NOTIFICATION_CENTER_BASE_URL= NOTIFICATION_CENTER_USERNAME= @@ -144,14 +126,10 @@ PROJECT_UPDATES_VERIFIED_REVOKED_DAYS=104 PROJECT_UPDATES_FIRST_REVOKE_BATCH_DATE=2021-01-22 PROJECT_REVOKE_SERVICE_ACTIVE=true -UPDATE_POWER_ROUND_CRONJOB_EXPRESSION=0 0 * * * -UPDATE_POWER_SNAPSHOT_SERVICE_ACTIVE=false - GIVBACK_MIN_FACTOR=0.5 GIVBACK_MAX_FACTOR=0.8 ONRAMPER_SECRET=secreto -ENABLE_GIV_POWER_TESTING=true THIRD_PARTY_PROJECTS_ADMIN_USER_ID=4 PROJECT_FILTERS_THREADS_POOL_CONCURRENCY=1 @@ -172,11 +150,6 @@ RECURRING_DONATION_VERIFICAITON_EXPIRATION_HOURS=24 POLYGON_MAINNET_NODE_HTTP_URL=https://polygon-rpc.com OPTIMISM_NODE_HTTP_URL=https://optimism-mainnet.public.blastapi.io -BALANCE_AGGREGATOR_BASE_URL=https://dev.serve.giveth.io/givpower-balance-aggregator -POWER_BALANCE_AGGREGATOR_ADAPTER=mock -NUMBER_OF_BALANCE_AGGREGATOR_BATCH=7 - - # ! millisecond cache, if we increase cache in test ENV we might get some errors in tests QF_ROUND_ESTIMATED_MATCHING_CACHE_DURATION=1 # ! millisecond cache, if we increase cache in test ENV we might get some errors in tests diff --git a/docs/powerBoosting.md b/docs/powerBoosting.md deleted file mode 100644 index f8e9c29c1..000000000 --- a/docs/powerBoosting.md +++ /dev/null @@ -1,275 +0,0 @@ -# Power Boosting Flow -[DB Tables](#tables) - -[DB Views](#materialized-views) - -[FAQ](#faq) - -## tables - -### power_boosting -When a user boosts a project we create a record in this table, this is some example data for that -#### query -``` -select id, "projectId","userId", "percentage" from power_boosting -order by id DESC -limit 5 -``` -#### result - -|id |projectId|userId|percentage| -|---|---------|------|----------| -|120|911 |68 |17 | -|119|137 |68 |19.09 | -|118|916 |68 |0 | -|117|223531 |68 |12.78 | -|116|2032 |68 |0 | - - -### power_round -This table just has when record to specify the current round number, we check to update this field with this cronjob expression -`UPDATE_POWER_ROUND_CRONJOB_EXPRESSION` the value of this in staging ENV is `0 */2 * * * *` it means every two minutes - -#### query - -``` -select * from power_round -``` -#### result - -|id |round| -|--- |-----| -|true | 1777| - -### pg_cron job -`pg_cron` extensions help reliably and precisely taking snapshots by calling `TAKE_POWER_BOOSTING_SNAPSHOT` procedure on time. -``` - CREATE EXTENSION IF NOT EXISTS PG_CRON; - - GRANT USAGE ON SCHEMA CRON TO POSTGRES; - - SELECT CRON.SCHEDULE( - '${POWER_BOOSTING_SNAPSHOT_TASK_NAME}', - '${cronJobExpression}', - $$CALL public."TAKE_POWER_BOOSTING_SNAPSHOT"()$$ - ); -``` - -Snapshots are taken by calling "TAKE_POWER_BOOSTING_SNAPSHOT" procedure by postgres cron job by the help of pg_cron extension. -The procedure saves a new record of `power_snapshot` with a snapshot time in the its time field. -The procedures also copies `power_boosting` table values corresponding to verified projects in `power_boosting_snapshot` table. - -And cronjob calls this: [procedure](https://github.com/Giveth/impact-graph/blob/staging/migration/1663594895751-takePowerSnapshotProcedure.ts) - -### power_snapshot -In each round, we create some snapshots. For instance, in staging (GIVeconomy deployment) each round lasts **20 min** and we take a snapshot every **5 min**, -so each round consists of 4 snapshots. In production that would be one snapshot every 4 hours, and a round duration of 14 days. - - -Each new `power_snapshot` record has empty blockNumber and roundNumber values. The impact-graph backend fills those fields by running a cron job with the expression -`FILL_BLOCK_NUMBERS_OF_SNAPSHOTS_CRONJOB_EXPRESSION` that in Staging ENV is set to `3 * * * * *` it means every minute (at the 3rd second) impact-graph searches for -incomplete `power_snapshot` records and make them complete by blockNumber and roundNumber values corresponding to its time field. - -#### query - -``` -select id, "blockNumber", "roundNumber" from power_snapshot -order by id DESC -limit 8 -``` - -#### result - -|id |blockNumber|roundNumber| -|---|-----------|-----------| -|7055|24935917 |1778 | -|7054|24935873 |1778 | -|7053|24935831 |1778 | -|7052|24935787 |1778 | -|7051|24935743 |1777 | -|7050|24935699 |1777 | -|7049|24935655 |1777 | -|7048|24935611 |1777 | - -### power_boosting_snapshot -As mentioned above, some `power_boosting_snapshot` records are created during `TAKE_POWER_BOOSTING_SNAPSHOT` procedure call to save boosting to verified projects at the time of the snapshot. -Each created `power_boosting_snapshot` record, created by `TAKE_POWER_BOOSTING_SNAPSHOT` procedure, points to the same `power_snapshot` record created at the same procedure call. - -#### query - -``` -select * from power_boosting_snapshot -order by id DESC -limit 5 -``` - -#### result - -|id |userId |projectId|powerSnapshotId|percentage| -|---|--------|---------|---------------|----------| -|602146|67 |915 |7057 |5.28 | -|602145|67 |137 |7057 |4.96 | -|602144|67 |911 |7057 |11.61 | -|602143|67 |929 |7057 |4.98 | -|602142|67 |223521 |7057 |2.69 | - -### power_balance_snapshot -With the above data we know in each snapshot which user boosted what percentage to a project, but we need to know -the givPower balance of users in each snapshot in order to determine **totalPower** for the project and then calculate the **projectRank** - - -We check with this cronjob expression `FILL_POWER_SNAPSHOT_BALANCE_CRONJOB_EXPRESSION` that in Staging ENV is `20 */5 * * * *` -means every **5 min** to fill power balance , for users that boosted a project in a snapshot but their corresponding balance at the time of the snapshot is not filled. - -How we fill the balance? We already have filled the **blockNumber** in powerSnapshot so we -call the subgraph and ask user's balance in that specific block - -#### query - -``` -select * from power_balance_snapshot -order by id DESC -limit 5 -``` - -#### result - -|id |userId |powerSnapshotId|balance| -|---|--------|---------------|-------| -|86112|255 |7059 |59509.14| -|86111|157 |7059 |19103.79| -|86110|379 |7059 |100538.4| -|86109|66 |7059 |171808.73| -|86108|168 |7059 |80313.22| - -## Materialized views -To calculate the project boost value, per user and project total, it's not optimal to run the query on each -request. Therefore, we have defined two materialized views to avoid data redundancy and utilize indexing features. - - -### user_project_power_view -We have created that with this [migration](https://github.com/Giveth/impact-graph/blob/staging/migration/1662877385339-UserProjectPowerView.ts) - -This view has all calculations of **power_round**, **power_balance_snapshot**, **power_boosting_snapshot** -So with this view you would know each person how much givPower boosted to a project, we should this data -to show it in the GIVpower tab in single project view. - -We refresh this view with `UPDATE_POWER_ROUND_CRONJOB_EXPRESSION` cron job expression that is `0 */2 * * * *` -means every **2 min** - - -#### query - -``` -select * from user_project_power_view -limit 5 -``` - -#### result - -|id |round |projectId|userId|boostedPower| -|---|--------|---------|------|------------| -|1 |1780 |82 |66 |85904.365 | -|2 |1780 |82 |67 |0 | -|3 |1780 |82 |68 |0 | -|4 |1780 |82 |168 |0 | -|5 |1780 |82 |246 |1555.1685 | - - - -### project_power_view -We have created that with this [migration](https://github.com/Giveth/impact-graph/blob/staging/migration/1662915983383-ProjectPowerView.ts) - -This view is like the above one, but calculates projects total power and rank them based on this value. We use it to join project query with this data and sort the result. - -We refresh this view with `UPDATE_POWER_ROUND_CRONJOB_EXPRESSION` cron job expression that is `0 */2 * * * *` -means every **2 min** - -#### query - -``` -select * from project_power_view -limit 5 -``` - -#### result - -|projectId|totalPower|powerRank|round| -|---------|----------|---------|-----| -|918 |457191.48718100006|1 |1779 | -|137 |169562.950621|2 |1779 | -|911 |158504.983757|3 |1779 | -|82 |155876.872369|4 |1779 | -|223531 |129154.467198|5 |1779 | - - -### project_future_power_view -We have created that with this [migration](https://github.com/Giveth/impact-graph/blob/staging/migration/1667732038996-ProjectFuturePowerView.ts) - -We refresh this view with `UPDATE_POWER_ROUND_CRONJOB_EXPRESSION` cron job expression that is `0 */2 * * * *` -means every **2 min** - -In this view despite **project_power_view**, we use next round value to join with snapshots. -Therefore, users would know what will be the rank of the project in the next round. - -#### query - -``` -select * from project_future_power_view -limit 5 -``` - -#### result - -|projectId|totalPower|powerRank|round| -|---------|----------|---------|-----| -|918 |457191.48718100006|1 |1779 | -|137 |169562.950621|2 |1779 | -|911 |158504.983757|3 |1779 | -|82 |155876.872369|4 |1779 | -|223531 |129154.467198|5 |1779 | - -#### Power Boosting Snapshot, Power Balance Snapshot and Power Snapshot Historic tables - -Eventually our snapshot tables will be filled with a lot of information, -this degrades the performance of the queries and the server. To solve this -a cronjob will run periodically set by `ARCHIVE_POWER_BOOSTING_OLD_SNAPSHOT_DATA_CRONJOB_EXPRESSION` variable. - -This cronjob executes the procedure `ARCHIVE_POWER_BOOSTING_OLD_SNAPSHOT_DATA` that removes the data from `power_boosting_snapshot`, `power_balance_snapshot`, `power_snapshot` that is older than 2 rounds from the current powerRound. - -The deleted information is moved to the tables `power_balance_snapshot_history`, `power_boosting_snapshot_history` and `power_snapshot_history`. - -## Project status changes affection on power boosting and ranking - -### Cancelled -When a project becomes cancelled, we set all GIVpower allocations to (percentages) that project zero. -In that case, the allocation values are added to other projects were supported by corresponding users proportionally. -https://github.com/Giveth/giveth-dapps-v2/issues/1837 - -**PS** However, we dont delete the snapshots' history of GIVpower boosting. Therefore, if the project gets active and verified again, the history of GIVpower allocations before cancellation will be included in projects total GIVpower and ranking in the leaderboard. (but we thought it's as super rare case) - -### Unverified/Verified -When a project becomes unverified, we remove it from GIVpower ranking, and also we stop taking snapshot of GIVpower boosting to it. -Nevertheless, the project doesn't lose its boosting history. We will notify -users who had boosted the project and inform them that they project is not verified anymore and their GIVpower allocation are being wasted. https://github.com/Giveth/GIVeconomy/issues/749. - -If a project becomes verified again, it will have GIVpower rank and return back to the leaderboard. Moreover, GIVpower allocations to it will be recorded again in the next snapshots. (obviously it would not have any snapshot for the time that project -was unverified) -https://github.com/Giveth/GIVeconomy/issues/739 - -### Activate/Deactivate -When a project becomes deactivated by project owner or the admin, we exclude it from the GIVPower ranking -but keep taking snapshots from its boostings. Therefore, when it becomes activated again, it will appear in the ranking immediately. -https://github.com/Giveth/giveth-dapps-v2/issues/1839 - -## FAQ - -### What we do if a project that has some boosting got unverified? -When we want to create record for **power_boosting_snapshot** we dont save boostings on unverified projects, so when we -verify the project again from next snapshot we start to count them , so in the period that project was unverified -we haven't saved any power_boosting_snapshot for that project but immediately after it gets verified we start to save -power_boosting_snapshot for next round - -### What if givPower balance of a user who already boosted some project changes during round? -As we get some snapshot during each round, we calculate of user's balance in each snapshot and boosted values correspondingly. -So it doesn't matter, because we have the balance history for that purpose and the average of values of each round will be the final value. diff --git a/migration/1646302349926-createOrganisatioTokenTable.ts b/migration/1646302349926-createOrganisatioTokenTable.ts new file mode 100644 index 000000000..54edcd511 --- /dev/null +++ b/migration/1646302349926-createOrganisatioTokenTable.ts @@ -0,0 +1,30 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class createOrganisatioTokenTable1646302349926 + implements MigrationInterface +{ + async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TABLE IF NOT EXISTS organization_tokens_token + ( + "organizationId" integer NOT NULL, + "tokenId" integer NOT NULL, + CONSTRAINT "PK_b811802b9de817da8820f0e60f1" PRIMARY KEY ("organizationId", "tokenId"), + CONSTRAINT "FK_2f48b3f2fa2c4d25ab3aab7167e" FOREIGN KEY ("tokenId") + REFERENCES token (id) MATCH SIMPLE + ON UPDATE CASCADE + ON DELETE CASCADE, + CONSTRAINT "FK_c59fe0d2f965ccb09648c5d4a9c" FOREIGN KEY ("organizationId") + REFERENCES organization (id) MATCH SIMPLE + ON UPDATE CASCADE + ON DELETE CASCADE + ) + ;`); + } + + async down(queryRunner: QueryRunner): Promise { + if (await queryRunner.hasTable('organization_tokens_token')) { + await queryRunner.query(`DROP TABLE "organization_tokens_token"`); + } + } +} diff --git a/migration/1696421249293-add_isStableCoin_field_to_token_table.ts b/migration/1696421249293-add_isStableCoin_field_to_token_table.ts new file mode 100644 index 000000000..e14a88e9d --- /dev/null +++ b/migration/1696421249293-add_isStableCoin_field_to_token_table.ts @@ -0,0 +1,36 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class addIsStableCoinFieldToTokenTable1696421249293 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + // Add the isStableCoin column with a default value of false + await queryRunner.query(` + DO $$ + BEGIN + BEGIN + ALTER TABLE token ADD COLUMN "isStableCoin" boolean NOT NULL DEFAULT false; + EXCEPTION + WHEN duplicate_column THEN + -- Handle the error, or just do nothing to skip adding the column. + RAISE NOTICE 'Column "isStableCoin" already exists in "token".'; + END; + END $$; + `); + + // Update records to set "isStableCoin" to true based on symbol + await queryRunner.query(` + UPDATE token + SET "isStableCoin" = true + WHERE symbol IN ('USDC', 'USDT', 'DAI', 'GLO', 'pyUSD', 'XDAI', 'WXDAI', 'cUSD'); + `); + } + + public async down(queryRunner: QueryRunner): Promise { + // Remove the "isStableCoin" column + await queryRunner.query(` + ALTER TABLE token + DROP COLUMN "isStableCoin"; + `); + } +} diff --git a/migration/1701756190381-create_donationeth_user.ts b/migration/1701756190381-create_donationeth_user.ts new file mode 100644 index 000000000..4c43ba553 --- /dev/null +++ b/migration/1701756190381-create_donationeth_user.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +const donationDotEthAddress = '0x6e8873085530406995170Da467010565968C7C62'; // Address behind donation.eth ENS address; +const matchingFundDonationsFromAddress = + (process.env.MATCHING_FUND_DONATIONS_FROM_ADDRESS as string) || + donationDotEthAddress; + +export class createDonationethUser1701756190381 implements MigrationInterface { + async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + INSERT INTO public."user" ("walletAddress", "name", "loginType", "role") + VALUES ('${matchingFundDonationsFromAddress}', 'Donation.eth', 'wallet', 'restricted') + ON CONFLICT ("walletAddress") DO NOTHING + `); + } + + async down(queryRunner: QueryRunner): Promise { + if (!matchingFundDonationsFromAddress) { + throw new Error('Wallet address is not defined in the configuration.'); + } + + await queryRunner.query( + `DELETE FROM public."user" WHERE "walletAddress" = '${matchingFundDonationsFromAddress}'`, + ); + } +} diff --git a/migration/1713859866338-enable_pg_trgm_extension.ts b/migration/1713859866338-enable_pg_trgm_extension.ts new file mode 100644 index 000000000..64c650580 --- /dev/null +++ b/migration/1713859866338-enable_pg_trgm_extension.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class EnablePgTrgmExtension1713859866338 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query('CREATE EXTENSION IF NOT EXISTS pg_trgm'); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query('DROP EXTENSION IF EXISTS pg_trgm'); + } +} diff --git a/migration/1715086559930-add_pg_trgm_indexes.ts b/migration/1715086559930-add_pg_trgm_indexes.ts new file mode 100644 index 000000000..57890bf58 --- /dev/null +++ b/migration/1715086559930-add_pg_trgm_indexes.ts @@ -0,0 +1,29 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddPgTrgmIndexes1715086559930 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + 'CREATE INDEX if not exists trgm_idx_project_title ON project USING GIN (title gin_trgm_ops);', + ); + await queryRunner.query( + 'CREATE INDEX if not exists trgm_idx_project_description ON project USING GIN (description gin_trgm_ops);', + ); + await queryRunner.query( + 'CREATE INDEX if not exists trgm_idx_project_impact_location ON project USING GIN ("impactLocation" gin_trgm_ops);', + ); + await queryRunner.query( + 'CREATE INDEX if not exists trgm_idx_user_name ON public.user USING GIN ("name" gin_trgm_ops);', + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query('drop index if exists trgm_idx_project_title;'); + await queryRunner.query( + 'drop index if exists trgm_idx_project_description;', + ); + await queryRunner.query( + 'drop index if exists trgm_idx_project_impact_location;', + ); + await queryRunner.query('drop index if exists trgm_idx_user_name;'); + } +} diff --git a/migration/1717646357435-ProjectEstimatedMatchingView_V2.ts b/migration/1717646357435-ProjectEstimatedMatchingView_V2.ts new file mode 100644 index 000000000..084924bd5 --- /dev/null +++ b/migration/1717646357435-ProjectEstimatedMatchingView_V2.ts @@ -0,0 +1,60 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ProjectEstimatedMatchingViewV21717646357435 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + ` + DROP MATERIALIZED VIEW IF EXISTS project_estimated_matching_view; + `, + ); + await queryRunner.query( + ` + CREATE MATERIALIZED VIEW project_estimated_matching_view AS + SELECT + donations_by_user."projectId", + donations_by_user."qfRoundId", + SUM(donations_by_user."valueUsd") as "sumValueUsd", + COUNT(*) as "uniqueDonorsCount", + SUM(SQRT(donations_by_user."valueUsd")) as "sqrtRootSum", + POWER(SUM(SQRT(donations_by_user."valueUsd")), 2) as "sqrtRootSumSquared", + COUNT(donations_by_user."userId") as "donorsCount" + FROM ( + SELECT + "donation"."projectId", + "donation"."qfRoundId", + SUM("donation"."valueUsd") as "valueUsd", + "donation"."userId" + FROM + "donation" + INNER JOIN "user" ON "user"."id" = "donation"."userId" + INNER JOIN "qf_round" ON "qf_round"."id" = "donation"."qfRoundId" + WHERE + "donation"."status" = 'verified' + AND "donation"."createdAt" BETWEEN "qf_round"."beginDate" AND "qf_round"."endDate" + GROUP BY + "donation"."projectId", + "donation"."qfRoundId", + "donation"."userId" + ) as donations_by_user + GROUP BY + donations_by_user."projectId", + donations_by_user."qfRoundId"; + `, + ); + await queryRunner.query(` + CREATE INDEX idx_project_estimated_matching_project_id ON project_estimated_matching_view USING hash ("projectId"); + CREATE INDEX idx_project_estimated_matching_qf_round_id ON project_estimated_matching_view USING btree ("qfRoundId"); + CREATE UNIQUE INDEX idx_project_estimated_matching_unique ON project_estimated_matching_view ("projectId", "qfRoundId"); + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + ` + DROP MATERIALIZED VIEW project_estimated_matching_view; + `, + ); + } +} diff --git a/migration/1717646612482-ProjectActualMatchingView_V16.ts b/migration/1717646612482-ProjectActualMatchingView_V16.ts new file mode 100644 index 000000000..e73d17e24 --- /dev/null +++ b/migration/1717646612482-ProjectActualMatchingView_V16.ts @@ -0,0 +1,184 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ProjectActualMatchingViewV161717646612482 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + DROP MATERIALIZED VIEW IF EXISTS project_actual_matching_view; + CREATE MATERIALIZED VIEW project_actual_matching_view AS + + + WITH ProjectsAndRounds AS ( + SELECT + p.id AS "projectId", + u.email, + p.slug, + p.title, + qr.id as "qfId", + qr."minimumPassportScore", + qr."eligibleNetworks", + STRING_AGG(DISTINCT CONCAT(pa."networkId", '-', pa."address"), ', ') AS "networkAddresses" + FROM + public.project p + INNER JOIN project_qf_rounds_qf_round pqrq ON pqrq."projectId" = p.id + INNER JOIN public."user" u on p."adminUserId" = u.id + INNER JOIN public.qf_round qr on qr.id = pqrq."qfRoundId" + LEFT JOIN project_address pa ON pa."projectId" = p.id AND pa."networkId" = ANY(qr."eligibleNetworks") AND pa."isRecipient" = true + group by + p.id, + u.email, + qr.id + ), + DonationsBeforeAnalysis AS ( + SELECT + par."projectId", + par.slug, + par.title, + par."qfId", + par."email", + par."networkAddresses", + par."minimumPassportScore" as "minimumPassportScore", + COALESCE(SUM(d."valueUsd"), 0) AS "allUsdReceived", + COUNT(DISTINCT CASE WHEN d."fromWalletAddress" IS NOT NULL THEN d."fromWalletAddress" END) AS "totalDonors", + ARRAY_AGG(DISTINCT d.id) FILTER (WHERE d.id IS NOT NULL) AS "donationIdsBeforeAnalysis" + FROM + ProjectsAndRounds par + LEFT JOIN public.donation d ON d."projectId" = par."projectId" AND d."qfRoundId" = par."qfId" AND d."status" = 'verified' AND d."transactionNetworkId" = ANY(par."eligibleNetworks") + GROUP BY + par."projectId", + par.title, + par."networkAddresses", + par.slug, + par."qfId", + par."email", + par."minimumPassportScore" + ), + UserProjectDonations AS ( + SELECT + par."projectId", + par."qfId" AS "qfRoundId", + d2."userId", + d2."fromWalletAddress", + d2."qfRoundUserScore", + COALESCE(SUM(d2."valueUsd"), 0) AS "totalValueUsd", + ARRAY_AGG(DISTINCT d2.id) FILTER (WHERE d2.id IS NOT NULL) AS "userDonationIds" + FROM + ProjectsAndRounds par + LEFT JOIN public.donation d2 ON d2."projectId" = par."projectId" AND d2."qfRoundId" = par."qfId" AND d2."status" = 'verified' AND d2."transactionNetworkId" = ANY(par."eligibleNetworks") + GROUP BY + par."projectId", + par."qfId", + d2."userId", + d2."fromWalletAddress", + d2."qfRoundUserScore" + ), + QualifiedUserDonations AS ( + SELECT + upd."userId", + upd."fromWalletAddress", + upd."projectId", + upd."qfRoundId", + upd."totalValueUsd", + upd."userDonationIds", + upd."qfRoundUserScore" + FROM + UserProjectDonations upd + WHERE + upd."totalValueUsd" >= (SELECT "minimumValidUsdValue" FROM public.qf_round WHERE id = upd."qfRoundId") + AND upd."qfRoundUserScore" >= (SELECT "minimumPassportScore" FROM public.qf_round WHERE id = upd."qfRoundId") + AND NOT EXISTS ( + SELECT 1 + FROM project_fraud pf + WHERE pf."projectId" = upd."projectId" + AND pf."qfRoundId" = upd."qfRoundId" + ) + AND NOT EXISTS ( + SELECT 1 + FROM sybil s + WHERE s."userId" = upd."userId" + AND s."qfRoundId" = upd."qfRoundId" + ) + AND NOT EXISTS ( + SELECT 1 + FROM project normal_project + JOIN project_address ON normal_project."id" = project_address."projectId" + WHERE normal_project."statusId" = 5 AND normal_project."reviewStatus" = 'Listed' + AND lower(project_address."address") = lower(upd."fromWalletAddress") + ) + AND NOT EXISTS ( + SELECT 1 + FROM project_address pa + INNER JOIN project_qf_rounds_qf_round pqrq ON pa."projectId" = pqrq."projectId" + WHERE pqrq."qfRoundId" = upd."qfRoundId" -- Ensuring we're looking at the same QF round + AND lower(pa."address") = lower(upd."fromWalletAddress") + AND pa."isRecipient" = true + ) + + ), + DonationIDsAggregated AS ( + SELECT + qud."projectId", + qud."qfRoundId", + ARRAY_AGG(DISTINCT unnested_ids) AS uniqueDonationIds + FROM + QualifiedUserDonations qud, + LATERAL UNNEST(qud."userDonationIds") AS unnested_ids + GROUP BY qud."projectId", qud."qfRoundId" + ), + DonationsAfterAnalysis AS ( + SELECT + da."projectId", + da.slug, + da.title, + da."qfId", + COALESCE(SUM(qud."totalValueUsd"), 0) AS "allUsdReceivedAfterSybilsAnalysis", + COUNT(DISTINCT qud."fromWalletAddress") AS "uniqueQualifiedDonors", + SUM(SQRT(qud."totalValueUsd")) AS "donationsSqrtRootSum", + POWER(SUM(SQRT(qud."totalValueUsd")), 2) as "donationsSqrtRootSumSquared", + dia.uniqueDonationIds AS "donationIdsAfterAnalysis", + ARRAY_AGG(DISTINCT qud."userId") AS "uniqueUserIdsAfterAnalysis", + ARRAY_AGG(qud."totalValueUsd") AS "totalValuesOfUserDonationsAfterAnalysis" + FROM + DonationsBeforeAnalysis da + LEFT JOIN QualifiedUserDonations qud ON da."projectId" = qud."projectId" AND da."qfId" = qud."qfRoundId" + LEFT JOIN DonationIDsAggregated dia ON da."projectId" = dia."projectId" AND da."qfId" = dia."qfRoundId" + GROUP BY + da."projectId", + da.slug, + da.title, + da.email, + da."qfId", + dia."uniquedonationids", + da."networkAddresses" + ) + + SELECT + da."projectId", + da.title, + da.email, + da.slug, + da."networkAddresses", + da."qfId" AS "qfRoundId", + da."donationIdsBeforeAnalysis", + da."allUsdReceived", + da."totalDonors", + daa."donationIdsAfterAnalysis", + daa."allUsdReceivedAfterSybilsAnalysis", + daa."uniqueQualifiedDonors", + daa."donationsSqrtRootSum", + daa."donationsSqrtRootSumSquared", + daa."uniqueUserIdsAfterAnalysis", + daa."totalValuesOfUserDonationsAfterAnalysis" + FROM + DonationsBeforeAnalysis da + INNER JOIN DonationsAfterAnalysis daa ON da."projectId" = daa."projectId" AND da."qfId" = daa."qfId"; + + CREATE INDEX idx_project_actual_matching_project_id ON project_actual_matching_view USING hash ("projectId"); + CREATE INDEX idx_project_actual_matching_qf_round_id ON project_actual_matching_view USING hash ("qfRoundId"); + CREATE UNIQUE INDEX idx_project_actual_matching_unique ON project_actual_matching_view ("projectId", "qfRoundId"); + `); + } + + public async down(_queryRunner: QueryRunner): Promise {} +} diff --git a/package.json b/package.json index bae95c346..e2ca1d90c 100644 --- a/package.json +++ b/package.json @@ -119,8 +119,6 @@ "test:projectEntity": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/entities/project.test.ts", "test:projectValidators": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/utils/validators/projectValidator.test.ts", "test:onramperWebhook": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/onramper/webhookHandler.test.ts", - "test:powerSnapshotRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/powerSnapshotRepository.test.ts", - "test:instantBoostingRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/instantBoostingRepository.test.ts", "test:donationTracker": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/segment/DonationTracker.test.ts", "test:userRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/userRepository.test.ts", "test:statusReasonRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/statusReasonRepository.test.ts", @@ -150,30 +148,16 @@ "test:qfRoundResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/qfRoundResolver.test.ts", "test:qfRoundHistoryResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/qfRoundHistoryResolver.test.ts", "test:projectVerificationResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/projectVerificationFormResolver.test.ts", - "test:givPowerSubgraphAdapter": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/adapters/givpowerSubgraph/givPowerSubgraphAdapter.test.ts", "test:projectRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/projectRepository.test.ts", "test:projectUpdateRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/projectUpdateRepository.test.ts", - "test:powerBalanceSnapshotRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/powerBalanceSnapshotRepository.test.ts", - "test:powerBoostingRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/powerBoostingRepository.test.ts", - "test:previousRoundRankRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/previousRoundRankRepository.test.ts", "test:broadcastNotificationRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/broadcastNotificationRepository.test.ts", - "test:userPowerRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/userPowerRepository.test.ts", - "test:powerRoundRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/powerRoundRepository.test.ts", - "test:userProjectPowerRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/userProjectPowerViewRepository.test.ts", - "test:projectPowerRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/projectPowerViewRepository.test.ts", "test:projectAddressRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/projectAddressRepository.test.ts", "test:anchorContractAddressRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/anchorContractAddressRepository.test.ts", "test:recurringDonationRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/recurringDonationRepository.test.ts", "test:userPassportScoreRepository": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/userPassportScoreRepository.test.ts", "test:recurringDonationService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/recurringDonationService.test.ts", - "test:dbCronRepository": "NODE_ENV=test mocha -t 90000 ./test/pre-test-scripts.ts ./src/repositories/dbCronRepository.test.ts", - "test:powerBoostingResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/powerBoostingResolver.test.ts", - "test:userProjectPowerResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/userProjectPowerResolver.test.ts", - "test:projectPowerResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/projectPowerResolver.test.ts", - "test:instantPowerResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/instantPowerResolver.test.ts", "test:anchorContractAddressResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/anchorContractAddressResolver.test.ts", "test:recurringDonationResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/recurringDonationResolver.test.ts", - "test:fillSnapshotBalance": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/cronJobs/fillSnapshotBalances.test.ts", "test:donationService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/repositories/qfRoundHistoryRepository.test.ts ./src/services/donationService.test.ts", "test:draftDonationService": "NODE_ENV=test mocha -t 99999 ./test/pre-test-scripts.ts src/services/chains/evm/draftDonationService.test.ts src/repositories/draftDonationRepository.test.ts src/workers/draftDonationMatchWorker.test.ts src/resolvers/draftDonationResolver.test.ts", "test:draftDonationWorker": "NODE_ENV=test mocha -t 99999 ./test/pre-test-scripts.ts src/workers/draftDonationMatchWorker.test.ts", @@ -181,18 +165,14 @@ "test:userService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/userService.test.ts", "test:lostDonations": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/cronJobs/importLostDonationsJob.test.ts", "test:reactionsService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/reactionsService.test.ts", - "test:powerSnapshotService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/powerSnapshotServices.test.ts", "test:transactionsService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts src/services/chains/index.test.ts", "test:projectViewService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts src/services/projectViewService.test.ts", "test:transactionsService:evm": "NODE_ENV=test mocha ./test/pre-test-scripts.ts src/services/chains/evm/transactionService.test.ts", "test:transactionsService:solana": "NODE_ENV=test mocha ./test/pre-test-scripts.ts src/services/chains/solana/transactionService.test.ts", "test:projectUpdatesService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/projectUpdatesService.test.ts", - "test:powerBoostingService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/powerBoostingService.test.ts", "test:blockByDateService": "NODE_ENV=test mocha ./src/services/blockByDateService.test.ts", - "test:instantPowerBoostingService": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/instantBoostingServices.test.ts", "test:actualMatchingFundView": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/services/actualMatchingFundView.test.ts", "test:categoryResolver": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/resolvers/categoryResolver.test.ts", - "test:givpower": "NODE_ENV=test mocha -b -t 30000 ./test/pre-test-scripts.ts ./src/repositories/powerBoostingRepository.test.ts ./src/repositories/userPowerRepository.test.ts ./src/repositories/powerRoundRepository.test.ts ./src/repositories/userProjectPowerViewRepository.test.ts ./src/repositories/projectPowerViewRepository.test.ts ./src/resolvers/powerBoostingResolver.test.ts ./src/resolvers/userProjectPowerResolver.test.ts ./src/resolvers/projectPowerResolver.test.ts ./src/adapters/givpowerSubgraph/givPowerSubgraphAdapter.test.ts ./src/repositories/projectRepository.test.ts ./src/resolvers/projectResolver.test.ts ./src/resolvers/projectResolver.allProject.test.ts ./src/repositories/dbCronRepository.test.ts", "test:apiGive": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/routers/apiGivRoutes.test.ts", "test:adminJs": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/server/adminJs/**/*.test.ts ", "test:adminJsRolePermissions": "NODE_ENV=test mocha ./test/pre-test-scripts.ts ./src/server/adminJs/adminJsPermissions.test.ts", diff --git a/src/adapters/adaptersFactory.ts b/src/adapters/adaptersFactory.ts index 7c5964527..c93bc1ea2 100644 --- a/src/adapters/adaptersFactory.ts +++ b/src/adapters/adaptersFactory.ts @@ -8,15 +8,10 @@ import { TwitterAdapter } from './oauth2/twitterAdapter'; import { NotificationAdapterInterface } from './notifications/NotificationAdapterInterface'; import { NotificationCenterAdapter } from './notifications/NotificationCenterAdapter'; import { MockNotificationAdapter } from './notifications/MockNotificationAdapter'; -import { GivPowerSubgraphAdapter } from './givpowerSubgraph/givPowerSubgraphAdapter'; -import { GivPowerSubgraphAdapterMock } from './givpowerSubgraph/givPowerSubgraphAdapterMock'; import { ChainvineAdapter } from './chainvine/chainvineAdapter'; import { ChainvineMockAdapter } from './chainvine/chainvineMockAdapter'; -import { IGivPowerSubgraphAdapter } from './givpowerSubgraph/IGivPowerSubgraphAdapter'; import { GitcoinAdapter } from './gitcoin/gitcoinAdapter'; import { GitcoinMockAdapter } from './gitcoin/gitcoinMockAdapter'; -import { GivPowerBalanceAggregatorAdapter } from './givPowerBalanceAggregator/givPowerBalanceAggregatorAdapter'; -import { GivPowerBalanceAggregatorAdapterMock } from './givPowerBalanceAggregator/givPowerBalanceAggregatorAdapterMock'; import { DonationSaveBackupAdapter } from './donationSaveBackup/donationSaveBackupAdapter'; import { DonationSaveBackupMockAdapter } from './donationSaveBackup/DonationSaveBackupMockAdapter'; import { SuperFluidAdapter } from './superFluid/superFluidAdapter'; @@ -61,22 +56,6 @@ export const getNotificationAdapter = (): NotificationAdapterInterface => { } }; -export const givPowerSubgraphAdapter = new GivPowerSubgraphAdapter(); -export const givPowerSubgraphAdapterMock = new GivPowerSubgraphAdapterMock(); - -export const getGivPowerSubgraphAdapter = (): IGivPowerSubgraphAdapter => { - switch (process.env.GIV_POWER_SUBGRAPH_ADAPTER) { - case 'givPower': - return givPowerSubgraphAdapter; - case 'mock': - return givPowerSubgraphAdapterMock; - default: - throw new Error( - i18n.__(translationErrorMessagesKeys.SPECIFY_GIV_POWER_ADAPTER), - ); - } -}; - const chainvineAdapter = new ChainvineAdapter(); const mockChainvineAdapter = new ChainvineMockAdapter(); @@ -105,21 +84,6 @@ export const getGitcoinAdapter = () => { } }; -export const powerBalanceAggregator = new GivPowerBalanceAggregatorAdapter(); -export const mockPowerBalanceAggregator = - new GivPowerBalanceAggregatorAdapterMock(); - -export const getPowerBalanceAggregatorAdapter = () => { - switch (process.env.POWER_BALANCE_AGGREGATOR_ADAPTER) { - case 'powerBalanceAggregator': - return powerBalanceAggregator; - case 'mock': - return mockPowerBalanceAggregator; - default: - return mockPowerBalanceAggregator; - } -}; - const donationSaveBackupAdapter = new DonationSaveBackupAdapter(); const mockDonationSaveBackupAdapter = new DonationSaveBackupMockAdapter(); diff --git a/src/adapters/givPowerBalanceAggregator/givPowerBalanceAggregatorAdapter.ts b/src/adapters/givPowerBalanceAggregator/givPowerBalanceAggregatorAdapter.ts deleted file mode 100644 index 69c2e4df1..000000000 --- a/src/adapters/givPowerBalanceAggregator/givPowerBalanceAggregatorAdapter.ts +++ /dev/null @@ -1,120 +0,0 @@ -import axios from 'axios'; -import { - BalancesAtTimestampInputParams, - BalanceResponse, - BalanceUpdatedAfterDateInputParams, - LatestBalanceInputParams, - NetworksInputParams, - IGivPowerBalanceAggregator, -} from '../../types/GivPowerBalanceAggregator'; -import { logger } from '../../utils/logger'; -import { formatGivPowerBalance } from '../givpowerSubgraph/givPowerSubgraphAdapter'; - -const formatResponse = (balance: { - address: string; - balance: string; - update_at: Date; - networks: number[]; -}): BalanceResponse => { - return { - address: balance.address, - balance: formatGivPowerBalance(balance.balance), - updatedAt: new Date(balance.update_at), - networks: balance.networks, - }; -}; - -export class GivPowerBalanceAggregatorAdapter - implements IGivPowerBalanceAggregator -{ - private baseUrl: string; - - constructor() { - this.baseUrl = process.env.BALANCE_AGGREGATOR_BASE_URL!; - if (!this.baseUrl) { - throw new Error( - 'BALANCE_AGGREGATOR_BASE_URL environment variable is not set', - ); - } - } - - async getAddressesBalance( - params: BalancesAtTimestampInputParams, - ): Promise { - try { - const data = { - timestamp: params.timestamp, - network: params.network, - networks: params.networks, - addresses: params.addresses.join(','), - }; - const response = await axios.get( - `${this.baseUrl}/power-balance/by-timestamp`, - { - params: data, - }, - ); - return response.data.map(balance => formatResponse(balance)); - } catch (e) { - logger.error('getBalanceOfAnAddress >> error', e); - throw e; - } - } - - async getLatestBalances( - params: LatestBalanceInputParams, - ): Promise { - const data = { - network: params.network, - networks: params.networks, - addresses: params.addresses.join(','), - }; - try { - const response = await axios.get(`${this.baseUrl}/power-balance`, { - params: data, - }); - return response.data.map(balance => formatResponse(balance)); - } catch (e) { - logger.error('getLatestBalanceOfAnAddress >> error', e); - throw e; - } - } - - async getBalancesUpdatedAfterDate( - params: BalanceUpdatedAfterDateInputParams, - ): Promise { - try { - const response = await axios.get( - `${this.baseUrl}/power-balance/updated-after-date`, - { - params, - }, - ); - const responseData = response.data; - const result: BalanceResponse[] = responseData.balances.map( - (balance: any) => formatResponse(balance), - ); - return result; - } catch (e) { - logger.error('getBalancesUpdatedAfterASpecificDate >> error', e); - throw e; - } - } - - async getLeastIndexedBlockTimeStamp( - params: NetworksInputParams, - ): Promise { - try { - const response = await axios.get( - `${this.baseUrl}/fetch-state/least-indexed-block-timestamp`, - { - params, - }, - ); - return response.data; - } catch (e) { - logger.error('getLeastIndexedBlockTimeStamp >> error', e); - throw e; - } - } -} diff --git a/src/adapters/givPowerBalanceAggregator/givPowerBalanceAggregatorAdapterMock.ts b/src/adapters/givPowerBalanceAggregator/givPowerBalanceAggregatorAdapterMock.ts deleted file mode 100644 index 8af86f797..000000000 --- a/src/adapters/givPowerBalanceAggregator/givPowerBalanceAggregatorAdapterMock.ts +++ /dev/null @@ -1,70 +0,0 @@ -import _ from 'lodash'; -import { - BalancesAtTimestampInputParams, - BalanceResponse, - LatestBalanceInputParams, - IGivPowerBalanceAggregator, -} from '../../types/GivPowerBalanceAggregator'; -import { convertTimeStampToSeconds } from '../../utils/utils'; - -export class GivPowerBalanceAggregatorAdapterMock - implements IGivPowerBalanceAggregator -{ - async getAddressesBalance( - params: BalancesAtTimestampInputParams, - ): Promise { - if ( - params.addresses.length > - Number(process.env.NUMBER_OF_BALANCE_AGGREGATOR_BATCH) - ) { - throw new Error( - 'addresses length can not be greater than NUMBER_OF_BALANCE_AGGREGATOR_BATCH that is defined in .env', - ); - } - return _.uniq(params.addresses).map(address => { - return { - address, - balance: 13, // Just an example balance - updatedAt: new Date('2023-08-10T16:18:02.655Z'), - networks: [100], - }; - }); - } - - async getLatestBalances( - params: LatestBalanceInputParams, - ): Promise { - // Mocked data - return _.uniq(params.addresses).map(address => { - return { - address, - balance: 200, // Just another example balance - updatedAt: new Date('2023-08-10T16:18:02.655Z'), - networks: params.network ? [Number(params.network)] : [100], - }; - }); - } - - async getBalancesUpdatedAfterDate(): Promise { - // Mocked data - return [ - { - address: '0x1234567890123456789012345678901234567890', - balance: 300, - updatedAt: new Date('2023-08-10T16:18:02.655Z'), - networks: [100], - }, - { - address: '0x1234567890123456789012345678901234567891', - balance: 400, - updatedAt: new Date('2023-09-10T16:18:02.655Z'), - networks: [100], - }, - ]; - } - - async getLeastIndexedBlockTimeStamp(): Promise { - // Mocked data - return convertTimeStampToSeconds(new Date().getTime()); - } -} diff --git a/src/adapters/givpowerSubgraph/IGivPowerSubgraphAdapter.ts b/src/adapters/givpowerSubgraph/IGivPowerSubgraphAdapter.ts deleted file mode 100644 index d511be26b..000000000 --- a/src/adapters/givpowerSubgraph/IGivPowerSubgraphAdapter.ts +++ /dev/null @@ -1,27 +0,0 @@ -export interface BlockInfo { - number: number; - timestamp: number; -} - -export interface UnipoolBalance { - balance: number; - updatedAt: number; -} - -export interface IGivPowerSubgraphAdapter { - getUserPowerBalanceAtBlockNumber(params: { - walletAddresses: string[]; - blockNumber: number; - }): Promise<{ - [walletAddress: string]: UnipoolBalance; - }>; - - getUserPowerBalanceUpdatedAfterTimestamp(params: { - timestamp: number; - blockNumber: number; - take: number; - skip: number; - }): Promise<{ [address: string]: UnipoolBalance }>; - - getLatestIndexedBlockInfo(): Promise; -} diff --git a/src/adapters/givpowerSubgraph/givPowerSubgraphAdapter.test.ts b/src/adapters/givpowerSubgraph/givPowerSubgraphAdapter.test.ts deleted file mode 100644 index bcdd7accd..000000000 --- a/src/adapters/givpowerSubgraph/givPowerSubgraphAdapter.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { assert } from 'chai'; -import { formatGivPowerBalance } from './givPowerSubgraphAdapter'; -import { generateRandomEtheriumAddress } from '../../../test/testUtils'; -import { givPowerSubgraphAdapter } from '../adaptersFactory'; - -describe( - 'getUserPowerBalanceInBlockNumber() test cases', - getUserPowerBalanceInBlockNumberTestCases, -); -describe('getLatestIndexedBlock test cases', getLatestIndexedBlockTestCases); - -describe( - 'getUserPowerBalanceUpdatedAfterTimestamp test cases', - getUserPowerBalanceUpdatedAfterTimestampTestCases, -); - -function getUserPowerBalanceInBlockNumberTestCases() { - it('should return correct info for block 24124422', async () => { - const firstAddress = '0x00d18ca9782be1caef611017c2fbc1a39779a57c'; - const secondAddress = '0x05a1ff0a32bc24265bcb39499d0c5d9a6cb2011c'; - const fakeWalletAddress = generateRandomEtheriumAddress(); - const result = - await givPowerSubgraphAdapter.getUserPowerBalanceAtBlockNumber({ - blockNumber: 24124422, - walletAddresses: [firstAddress, secondAddress, fakeWalletAddress], - }); - assert.equal(Object.keys(result).length, 3); - assert.equal(result[firstAddress].balance, 127095.68); - assert.equal(result[secondAddress].balance, 25000); - assert.equal(result[fakeWalletAddress].balance, 0); - }); - it('should return correct info for block 24344249', async () => { - const firstAddress = '0x00d18ca9782be1caef611017c2fbc1a39779a57c'; - const secondAddress = '0x05a1ff0a32bc24265bcb39499d0c5d9a6cb2011c'; - const fakeWalletAddress = generateRandomEtheriumAddress(); - const result = - await givPowerSubgraphAdapter.getUserPowerBalanceAtBlockNumber({ - blockNumber: 24344249, - walletAddresses: [firstAddress, secondAddress, fakeWalletAddress], - }); - assert.equal(Object.keys(result).length, 3); - assert.equal(result[firstAddress].balance, 171808.73); - assert.equal(result[secondAddress].balance, 25000); - assert.equal(result[fakeWalletAddress].balance, 0); - }); -} - -function getLatestIndexedBlockTestCases() { - it('should fetch latest block info', async () => { - const block = await givPowerSubgraphAdapter.getLatestIndexedBlockInfo(); - assert.isOk(block); - assert.isAbove(block.number, 0); - assert.isAbove(block.timestamp, 0); - }); -} - -async function getUserPowerBalanceUpdatedAfterTimestampTestCases() { - it('should return correct info for from timestamp 1680020855 at block 27723732', async () => { - const expectedBalances = [ - { - user: { - id: '0x8f48094a12c8f99d616ae8f3305d5ec73cbaa6b6', - }, - balance: '53814827464908927720392', - updatedAt: '1681744235', - }, - { - user: { - id: '0xc46c67bb7e84490d7ebdd0b8ecdaca68cf3823f4', - }, - balance: '1332998538238235687437229', - updatedAt: '1680021855', - }, - { - user: { - id: '0xcd192b61a8dd586a97592555c1f5709e032f2505', - }, - balance: '90459674185703962293876', - updatedAt: '1681744235', - }, - ]; - const queryBlockNumber = 27723732; - const lastSyncedTimestamp = 1680020855; - - const balances = - await givPowerSubgraphAdapter.getUserPowerBalanceUpdatedAfterTimestamp({ - blockNumber: queryBlockNumber, - timestamp: lastSyncedTimestamp, - take: 100, - skip: 0, - }); - - assert.equal(Object.keys(balances).length, expectedBalances.length); - for (const expectedBalance of expectedBalances) { - const balance = balances[expectedBalance.user.id]; - assert.isOk(balance); - assert.equal( - balance.balance, - formatGivPowerBalance(expectedBalance.balance), - ); - assert.equal(balance.updatedAt, +expectedBalance.updatedAt); - } - }); -} diff --git a/src/adapters/givpowerSubgraph/givPowerSubgraphAdapter.ts b/src/adapters/givpowerSubgraph/givPowerSubgraphAdapter.ts deleted file mode 100644 index 4e340e0b5..000000000 --- a/src/adapters/givpowerSubgraph/givPowerSubgraphAdapter.ts +++ /dev/null @@ -1,155 +0,0 @@ -import BigNumber from 'bignumber.js'; -import axios from 'axios'; -import { - BlockInfo, - IGivPowerSubgraphAdapter, - UnipoolBalance, -} from './IGivPowerSubgraphAdapter'; - -const _toBN = (n: string | number) => new BigNumber(n); -export const formatGivPowerBalance = (balance: string | number): number => - Number( - _toBN(balance) - .div(10 ** 18) - .toFixed(2), - ); - -const getPowerBalanceInBlockQuery = (params: { - addresses: Set; - blockNumber: number; - unipoolContractId: string; -}): string => { - const { addresses, blockNumber, unipoolContractId } = params; - const usersIn = - '[' + - Array.from(addresses) - .map(address => `"${address}"`) - .join(',') + - ']'; - return `query { - unipoolBalances( - first: ${addresses.size} - where: { - unipool: "${unipoolContractId.toLowerCase()}", - user_in: ${usersIn.toLowerCase()} - }, - block: {number:${blockNumber} } - ) { - balance - updatedAt - user { - id - } - } - }`; -}; - -const getPowerBalancesUpdatedAfterTimestampQuery = (params: { - timestamp: number; - blockNumber: number; - unipoolContractId: string; - take: number; - skip: number; -}): string => { - const { timestamp, blockNumber, unipoolContractId, take, skip } = params; - return `query { - unipoolBalances( - first: ${take} - skip: ${skip} - orderBy: updatedAt - orderDirection: asc - where: { - unipool: "${unipoolContractId.toLowerCase()}", - updatedAt_gt: ${timestamp} - }, - block: {number:${blockNumber} } - ) { - balance - updatedAt - user { - id - } - } - }`; -}; - -export class GivPowerSubgraphAdapter implements IGivPowerSubgraphAdapter { - async getUserPowerBalanceAtBlockNumber(params: { - walletAddresses: string[]; - blockNumber: number; - }): Promise<{ [address: string]: UnipoolBalance }> { - const { walletAddresses, blockNumber } = params; - const givPowerSubgraphUrl = process.env.GIV_POWER_SUBGRAPH_URL as string; - const unipoolContractId = process.env - .GIV_POWER_UNIPOOL_CONTRACT_ID as string; - const response = await axios.post(givPowerSubgraphUrl, { - query: getPowerBalanceInBlockQuery({ - addresses: new Set(walletAddresses), - blockNumber, - unipoolContractId, - }), - }); - const unipoolBalances = response.data.data.unipoolBalances; - const result: { [address: string]: UnipoolBalance } = {}; - unipoolBalances.forEach(unipoolBalance => { - const walletAddress = unipoolBalance.user.id; - result[walletAddress] = { - balance: formatGivPowerBalance(unipoolBalance.balance), - updatedAt: unipoolBalance.updatedAt, - }; - }); - walletAddresses.forEach(address => { - if (!result[address]) { - result[address] = { - balance: 0, - updatedAt: 0, - }; - } - }); - return result; - } - - async getUserPowerBalanceUpdatedAfterTimestamp(params: { - timestamp: number; - blockNumber: number; - take: number; - skip: number; - }): Promise<{ [address: string]: UnipoolBalance }> { - const givPowerSubgraphUrl = process.env.GIV_POWER_SUBGRAPH_URL as string; - const unipoolContractId = process.env - .GIV_POWER_UNIPOOL_CONTRACT_ID as string; - const query = getPowerBalancesUpdatedAfterTimestampQuery({ - ...params, - unipoolContractId, - }); - const response = await axios.post(givPowerSubgraphUrl, { - query, - }); - const unipoolBalances = response.data.data.unipoolBalances; - const result: { [address: string]: UnipoolBalance } = {}; - unipoolBalances.forEach(unipoolBalance => { - const walletAddress = unipoolBalance.user.id; - result[walletAddress] = { - balance: formatGivPowerBalance(unipoolBalance.balance), - updatedAt: unipoolBalance.updatedAt, - }; - }); - return result; - } - - async getLatestIndexedBlockInfo(): Promise { - const givPowerSubgraphUrl = process.env.GIV_POWER_SUBGRAPH_URL as string; - - const response = await axios.post(givPowerSubgraphUrl, { - query: `query { - _meta { - block { - number - timestamp - } - } - }`, - }); - return response.data.data._meta.block; - } -} diff --git a/src/adapters/givpowerSubgraph/givPowerSubgraphAdapterMock.ts b/src/adapters/givpowerSubgraph/givPowerSubgraphAdapterMock.ts deleted file mode 100644 index fb4a17e1a..000000000 --- a/src/adapters/givpowerSubgraph/givPowerSubgraphAdapterMock.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { - IGivPowerSubgraphAdapter, - BlockInfo, - UnipoolBalance, -} from './IGivPowerSubgraphAdapter'; -import { sleep } from '../../utils/utils'; - -export class GivPowerSubgraphAdapterMock implements IGivPowerSubgraphAdapter { - nextCallResult: any = null; - async getUserPowerBalanceAtBlockNumber(params: { - walletAddresses: string[]; - blockNumber: number; - }): Promise<{ [p: string]: UnipoolBalance }> { - if (this.nextCallResult) { - const customResult = this.nextCallResult; - this.nextCallResult = null; - return Promise.resolve(customResult); - } - const result: { [address: string]: UnipoolBalance } = {}; - params.walletAddresses.forEach(walletAddress => { - result[walletAddress] = { - balance: Math.floor(Math.random() * 1000), - updatedAt: Math.floor(Math.random() * 1000), - }; - }); - - // To simulate real adapter condition - await sleep(10); - return Promise.resolve(result); - } - - getLatestIndexedBlockInfo(): Promise { - if (this.nextCallResult) { - const customResult = this.nextCallResult; - this.nextCallResult = null; - return Promise.resolve(customResult); - } - return Promise.resolve({ timestamp: 1000, number: 1000 }); - } - - getUserPowerBalanceUpdatedAfterTimestamp(): Promise<{ - [p: string]: UnipoolBalance; - }> { - if (this.nextCallResult) { - const customResult = this.nextCallResult; - this.nextCallResult = null; - return Promise.resolve(customResult); - } - return Promise.resolve({}); - } -} diff --git a/src/adapters/notifications/MockNotificationAdapter.ts b/src/adapters/notifications/MockNotificationAdapter.ts index e47a3825c..eff3c531b 100644 --- a/src/adapters/notifications/MockNotificationAdapter.ts +++ b/src/adapters/notifications/MockNotificationAdapter.ts @@ -79,24 +79,6 @@ export class MockNotificationAdapter implements NotificationAdapterInterface { return Promise.resolve(undefined); } - projectBoosted(params: { projectId: number; userId: number }): Promise { - logger.debug('MockNotificationAdapter projectBoosted', { - projectId: params.projectId, - userId: params.userId, - }); - return Promise.resolve(undefined); - } - - projectBoostedBatch(params: { - projectIds: number[]; - userId: number; - }): Promise { - logger.debug('MockNotificationAdapter projectBoostedBatch', { - projectIds: params.projectIds, - userId: params.userId, - }); - return Promise.resolve(undefined); - } projectBadgeRevoked(params: { project: Project }): Promise { logger.debug('MockNotificationAdapter projectBadgeRevoked', { projectSlug: params.project.slug, diff --git a/src/adapters/notifications/NotificationAdapterInterface.ts b/src/adapters/notifications/NotificationAdapterInterface.ts index 7e19aaacb..959a616ef 100644 --- a/src/adapters/notifications/NotificationAdapterInterface.ts +++ b/src/adapters/notifications/NotificationAdapterInterface.ts @@ -73,11 +73,6 @@ export interface NotificationAdapterInterface { }): Promise; projectVerified(params: { project: Project }): Promise; - projectBoosted(params: { projectId: number; userId: number }): Promise; - projectBoostedBatch(params: { - projectIds: number[]; - userId: number; - }): Promise; projectBadgeRevoked(params: { project: Project }): Promise; projectBadgeRevokeReminder(params: { project: Project }): Promise; projectBadgeRevokeWarning(params: { project: Project }): Promise; diff --git a/src/adapters/notifications/NotificationCenterAdapter.ts b/src/adapters/notifications/NotificationCenterAdapter.ts index 1cc873ba6..5968d2935 100644 --- a/src/adapters/notifications/NotificationCenterAdapter.ts +++ b/src/adapters/notifications/NotificationCenterAdapter.ts @@ -345,43 +345,6 @@ export class NotificationCenterAdapter implements NotificationAdapterInterface { }); } - async projectBoosted(params: { - projectId: number; - userId: number; - }): Promise { - const { projectId, userId } = params; - const project = (await findProjectById(projectId)) as Project; - sendProjectRelatedNotificationsQueue.add({ - project: project as Project, - eventName: - project.adminUser?.id === userId - ? // We send different notifications when project owner or someone else boost the project https://github.com/Giveth/notification-center/issues/41 - NOTIFICATIONS_EVENT_NAMES.PROJECT_BOOSTED_BY_PROJECT_OWNER - : NOTIFICATIONS_EVENT_NAMES.PROJECT_BOOSTED, - - // With adding trackId to notification, notification-center would not create new notification - // If there is already a notification with this trackId in DB - trackId: generateTrackId({ - userId, - projectId: project?.id as number, - action: 'boostProject', - }), - }); - } - - async projectBoostedBatch(params: { - projectIds: number[]; - userId: number; - }): Promise { - const { userId, projectIds } = params; - for (const projectId of projectIds) { - await this.projectBoosted({ - userId, - projectId, - }); - } - } - async projectBadgeRevoked(params: { project: Project }): Promise { const { project } = params; const user = project.adminUser as User; @@ -1288,11 +1251,3 @@ const authorizationHeader = () => { password: notificationCenterPassword, }); }; - -const generateTrackId = (params: { - userId: number; - action: 'likeProject' | 'boostProject'; - projectId: number; -}): string => { - return `${params.action}-${params.projectId}-${params.userId}`; -}; diff --git a/src/analytics/analytics.ts b/src/analytics/analytics.ts index 44b40ffc4..bd3caef65 100644 --- a/src/analytics/analytics.ts +++ b/src/analytics/analytics.ts @@ -10,8 +10,6 @@ export enum NOTIFICATIONS_EVENT_NAMES { PROJECT_BADGE_REVOKE_WARNING = 'Project badge revoke warning', PROJECT_BADGE_REVOKE_LAST_WARNING = 'Project badge revoke last warning', PROJECT_BADGE_UP_FOR_REVOKING = 'Project badge up for revoking', - PROJECT_BOOSTED = 'Project boosted', - PROJECT_BOOSTED_BY_PROJECT_OWNER = 'Project boosted by project owner', PROJECT_VERIFIED = 'Project verified', PROJECT_VERIFIED_USERS_WHO_SUPPORT = 'Project verified - Users who supported', diff --git a/src/config.ts b/src/config.ts index be3f52e62..2e7e6a98c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -50,11 +50,6 @@ const envVars = [ 'OUR_SECRET', // 'XDAI_NODE_HTTP_URL', 'TRACE_FILE_UPLOADER_PASSWORD', - 'GIVPOWER_BOOSTING_USER_PROJECTS_LIMIT', - 'GIVPOWER_BOOSTING_PERCENTAGE_PRECISION', - 'GIVPOWER_ROUND_DURATION', - 'GIVBACK_MAX_FACTOR', - 'GIVBACK_MIN_FACTOR', 'DONATION_VERIFICAITON_EXPIRATION_HOURS', ]; @@ -91,9 +86,6 @@ interface requiredEnv { OUR_SECRET: string; XDAI_NODE_HTTP_URL: string; TRACE_FILE_UPLOADER_PASSWORD: string; - GIVPOWER_BOOSTING_USER_PROJECTS_LIMIT: string; - GIVPOWER_BOOSTING_PERCENTAGE_PRECISION: string; - GIVPOWER_ROUND_DURATION: string; } class Config { diff --git a/src/entities/campaign.ts b/src/entities/campaign.ts index 744d48d02..a47906f79 100644 --- a/src/entities/campaign.ts +++ b/src/entities/campaign.ts @@ -17,7 +17,6 @@ export enum CampaignSortingField { Oldest = 'Oldest', RecentlyUpdated = 'RecentlyUpdated', QualityScore = 'QualityScore', - GIVPower = 'GIVPower', } export enum CampaignFilterField { @@ -25,7 +24,6 @@ export enum CampaignFilterField { givingBlocksId = 'givingBlocksId', acceptFundOnGnosis = 'acceptFundOnGnosis', fromGivingBlock = 'fromGivingBlock', - boostedWithGivPower = 'boostedWithGivPower', } export enum CampaignType { diff --git a/src/entities/donation.ts b/src/entities/donation.ts index 1cf3b028b..5cf38db86 100644 --- a/src/entities/donation.ts +++ b/src/entities/donation.ts @@ -151,22 +151,6 @@ export class Donation extends BaseEntity { @Column({ type: 'real', nullable: true }) priceUsd: number; - @Field({ nullable: true }) - @Column({ type: 'real', nullable: true }) - givbackFactor: number; - - @Field({ nullable: true }) - @Column({ nullable: true }) - powerRound: number; - - @Field({ nullable: true }) - @Column({ type: 'real', nullable: true }) - projectRank?: number; - - @Field({ nullable: true }) - @Column({ type: 'real', nullable: true }) - bottomRankInRound?: number; - @Index() @Field(_type => Project) @ManyToOne(_type => Project, { eager: true }) diff --git a/src/entities/entities.ts b/src/entities/entities.ts index feeb0d0f6..31cf406ca 100644 --- a/src/entities/entities.ts +++ b/src/entities/entities.ts @@ -15,29 +15,12 @@ import { ProjectVerificationForm } from './projectVerificationForm'; import { ProjectAddress } from './projectAddress'; import { SocialProfile } from './socialProfile'; import { MainCategory } from './mainCategory'; -import { PowerBoosting } from './powerBoosting'; -import { UserProjectPowerView } from '../views/userProjectPowerView'; -import { ProjectUserInstantPowerView } from '../views/projectUserInstantPowerView'; -import { PowerRound } from './powerRound'; -import { ProjectPowerView } from '../views/projectPowerView'; -import { PowerSnapshot } from './powerSnapshot'; -import { PowerBalanceSnapshot } from './powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from './powerBoostingSnapshot'; -import { ProjectFuturePowerView } from '../views/projectFuturePowerView'; -import { PowerSnapshotHistory } from './powerSnapshotHistory'; -import { PowerBalanceSnapshotHistory } from './powerBalanceSnapshotHistory'; -import { PowerBoostingSnapshotHistory } from './powerBoostingSnapshotHistory'; -import { LastSnapshotProjectPowerView } from '../views/lastSnapshotProjectPowerView'; import { User } from './user'; import { Project, ProjectUpdate } from './project'; import { Reaction } from './reaction'; import BroadcastNotification from './broadcastNotification'; import { FeaturedUpdate } from './featuredUpdate'; import { Campaign } from './campaign'; -import { PreviousRoundRank } from './previousRoundRank'; -import { InstantPowerBalance } from './instantPowerBalance'; -import { InstantPowerFetchState } from './instantPowerFetchState'; -import { ProjectInstantPowerView } from '../views/projectInstantPowerView'; import { QfRound } from './qfRound'; import { ReferredEvent } from './referredEvent'; import { QfRoundHistory } from './qfRoundHistory'; @@ -80,35 +63,15 @@ export const getEntities = (): DataSourceOptions['entities'] => { ProjectSocialMedia, SocialProfile, MainCategory, - PowerBoosting, - PowerRound, - PowerSnapshot, - PowerBalanceSnapshot, - PowerBoostingSnapshot, - // View - UserProjectPowerView, - ProjectPowerView, - ProjectFuturePowerView, - LastSnapshotProjectPowerView, - ProjectInstantPowerView, - ProjectUserInstantPowerView, ProjectEstimatedMatchingView, ProjectActualMatchingView, // historic snapshots - PowerSnapshotHistory, - PowerBalanceSnapshotHistory, - PowerBoostingSnapshotHistory, BroadcastNotification, Campaign, - PreviousRoundRank, - - InstantPowerBalance, - InstantPowerFetchState, - QfRound, QfRoundHistory, Sybil, diff --git a/src/entities/instantPowerBalance.ts b/src/entities/instantPowerBalance.ts deleted file mode 100644 index b6b5090bb..000000000 --- a/src/entities/instantPowerBalance.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Field, ID, ObjectType } from 'type-graphql'; -import { - PrimaryGeneratedColumn, - Column, - Entity, - BaseEntity, - Index, -} from 'typeorm'; - -@Entity() -@ObjectType() -export class InstantPowerBalance extends BaseEntity { - @Field(_type => ID) - @PrimaryGeneratedColumn() - id: number; - - @Field(_type => ID) - @Column() - @Index({ unique: true }) - userId: number; - - @Field({ defaultValue: 0 }) - @Column('float') - balance: number; - - // the timestamp (of chain block) the balance value is update at - @Field({ nullable: true }) - @Column() - balanceAggregatorUpdatedAt: Date; -} diff --git a/src/entities/instantPowerFetchState.ts b/src/entities/instantPowerFetchState.ts deleted file mode 100644 index 31d4e0b5c..000000000 --- a/src/entities/instantPowerFetchState.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { BaseEntity, Check, Column, Entity, PrimaryColumn } from 'typeorm'; -import { Field, ObjectType } from 'type-graphql'; -import { ColumnBigIntTransformer } from '../utils/entities'; - -@Entity() -@ObjectType() -@Check('"id"') -export class InstantPowerFetchState extends BaseEntity { - @Field(_type => Boolean) - @PrimaryColumn() - id: boolean; - - @Field() - @Column('bigint', { - transformer: new ColumnBigIntTransformer(), - }) - maxFetchedUpdateAtTimestampMS: number; -} diff --git a/src/entities/powerBalanceSnapshot.ts b/src/entities/powerBalanceSnapshot.ts deleted file mode 100644 index 84750b46d..000000000 --- a/src/entities/powerBalanceSnapshot.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Field, ID, ObjectType } from 'type-graphql'; -import { - PrimaryGeneratedColumn, - Column, - Entity, - BaseEntity, - Index, - RelationId, - ManyToOne, -} from 'typeorm'; -import { PowerSnapshot } from './powerSnapshot'; - -@Entity() -@ObjectType() -@Index(['userId', 'powerSnapshotId'], { unique: true }) -// To improve the performance of the query, we need to add the following index -@Index(['powerSnapshotId', 'userId'], { where: 'balance IS NULL' }) -export class PowerBalanceSnapshot extends BaseEntity { - @Field(_type => ID) - @PrimaryGeneratedColumn() - id: number; - - @Field(_type => ID) - @Column() - userId: number; - - @Field() - @Column('float', { nullable: true }) - balance: number; - - @Field(_type => ID) - @RelationId( - (powerBalanceSnapshot: PowerBalanceSnapshot) => - powerBalanceSnapshot.powerSnapshot, - ) - @Column() - powerSnapshotId: number; - - @Field(_type => PowerSnapshot, { nullable: false }) - @ManyToOne(_type => PowerSnapshot, { nullable: false }) - powerSnapshot: PowerSnapshot; -} diff --git a/src/entities/powerBalanceSnapshotHistory.ts b/src/entities/powerBalanceSnapshotHistory.ts deleted file mode 100644 index 4a312aa28..000000000 --- a/src/entities/powerBalanceSnapshotHistory.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Field, ID, ObjectType } from 'type-graphql'; -import { Column, Entity, BaseEntity, PrimaryColumn } from 'typeorm'; - -@Entity() -@ObjectType() -export class PowerBalanceSnapshotHistory extends BaseEntity { - @Field(_type => ID) - @PrimaryColumn() - id: number; - - @Field(_type => ID) - @Column() - userId: number; - - @Field() - @Column('float') - balance: number; - - @Field(_type => ID) - @Column() - powerSnapshotId: number; -} diff --git a/src/entities/powerBoosting.ts b/src/entities/powerBoosting.ts deleted file mode 100644 index 932916e1c..000000000 --- a/src/entities/powerBoosting.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { - BaseEntity, - Column, - CreateDateColumn, - Entity, - Index, - ManyToOne, - PrimaryGeneratedColumn, - RelationId, - UpdateDateColumn, -} from 'typeorm'; -import { Field, Float, ID, ObjectType } from 'type-graphql'; -import { Max, Min, IsNumber } from 'class-validator'; -import { Project } from './project'; -import { User } from './user'; -import { ColumnNumericTransformer } from '../utils/entities'; - -@Entity() -@ObjectType() -@Index(['projectId', 'userId'], { unique: true }) -export class PowerBoosting extends BaseEntity { - @Field(_type => ID) - @PrimaryGeneratedColumn() - id: number; - - @Field(_type => Project) - @ManyToOne(_type => Project, { eager: true }) - project: Project; - - @Index() - @RelationId((powerBoosting: PowerBoosting) => powerBoosting.project) - @Column({ nullable: false }) - projectId: number; - - @Field(_type => User) - @ManyToOne(_type => User, { eager: true }) - user: User; - - @Index() - @RelationId((powerBoosting: PowerBoosting) => powerBoosting.user) - @Column({ nullable: false }) - userId: number; - - @Field(_type => Float) - @Column('numeric', { - precision: 5, // 100.00 - scale: 2, - transformer: new ColumnNumericTransformer(), - }) - // https://orkhan.gitbook.io/typeorm/docs/validation - @IsNumber() - @Min(0) - @Max(100) - percentage: number; - - @CreateDateColumn() - @Field() - createdAt: Date; - - @UpdateDateColumn() - @Field() - updatedAt: Date; -} diff --git a/src/entities/powerBoostingSnapshot.ts b/src/entities/powerBoostingSnapshot.ts deleted file mode 100644 index b033caaf2..000000000 --- a/src/entities/powerBoostingSnapshot.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { - BaseEntity, - Column, - Entity, - Index, - ManyToOne, - PrimaryGeneratedColumn, - RelationId, -} from 'typeorm'; -import { Field, Float, ID, ObjectType } from 'type-graphql'; -import { User } from './user'; -import { ColumnNumericTransformer } from '../utils/entities'; -import { PowerSnapshot } from './powerSnapshot'; - -@Entity() -@ObjectType() -@Index(['userId', 'projectId', 'powerSnapshotId'], { unique: true }) -export class PowerBoostingSnapshot extends BaseEntity { - @Field(_type => ID) - @PrimaryGeneratedColumn() - id: number; - - @Field(_type => ID) - @RelationId( - (powerBoostingSnapshot: PowerBoostingSnapshot) => - powerBoostingSnapshot.user, - ) - @Column() - userId: number; - - @Field(_type => User, { nullable: false }) - @ManyToOne(_type => User, { nullable: false }) - user: User; - - @Field(_type => ID) - @Column() - projectId: number; - - @Field(_type => ID) - @RelationId( - (powerBoostingSnapshot: PowerBoostingSnapshot) => - powerBoostingSnapshot.powerSnapshot, - ) - @Column() - powerSnapshotId: number; - - @Field(_type => PowerSnapshot, { nullable: true }) - @ManyToOne(_type => PowerSnapshot, { nullable: false }) - powerSnapshot: PowerSnapshot; - - @Field(_type => Float) - @Column('numeric', { - precision: 5, // 100.00 - scale: 2, - transformer: new ColumnNumericTransformer(), - }) - percentage: number; -} diff --git a/src/entities/powerBoostingSnapshotHistory.ts b/src/entities/powerBoostingSnapshotHistory.ts deleted file mode 100644 index bfb5f2bd7..000000000 --- a/src/entities/powerBoostingSnapshotHistory.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { BaseEntity, Column, Entity, PrimaryColumn } from 'typeorm'; -import { Field, Float, ID, ObjectType } from 'type-graphql'; -import { ColumnNumericTransformer } from '../utils/entities'; - -@Entity() -@ObjectType() -export class PowerBoostingSnapshotHistory extends BaseEntity { - @Field(_type => ID) - @PrimaryColumn() - id: number; - - @Field(_type => ID) - @Column() - userId: number; - - @Field(_type => ID) - @Column() - projectId: number; - - @Field(_type => ID) - @Column() - powerSnapshotId: number; - - @Field(_type => Float) - @Column('numeric', { - precision: 5, // 100.00 - scale: 2, - transformer: new ColumnNumericTransformer(), - }) - percentage: number; -} diff --git a/src/entities/powerRound.ts b/src/entities/powerRound.ts deleted file mode 100644 index 4a99876dd..000000000 --- a/src/entities/powerRound.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Field, ObjectType } from 'type-graphql'; -import { Column, Entity, BaseEntity, PrimaryColumn, Check } from 'typeorm'; - -@Entity() -@ObjectType() -@Check('"id"') -export class PowerRound extends BaseEntity { - @Field(_type => Boolean) - @PrimaryColumn() - id: boolean; - - @Field() - @Column({ type: 'integer' }) - round: number; -} diff --git a/src/entities/powerSnapshot.ts b/src/entities/powerSnapshot.ts deleted file mode 100644 index 332e9c7ef..000000000 --- a/src/entities/powerSnapshot.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Field, ID, Int, ObjectType } from 'type-graphql'; -import { - PrimaryGeneratedColumn, - Column, - Entity, - BaseEntity, - Index, - OneToMany, -} from 'typeorm'; -import { PowerBoostingSnapshot } from './powerBoostingSnapshot'; -import { PowerBalanceSnapshot } from './powerBalanceSnapshot'; -import { ColumnDateTransformer } from '../utils/entities'; - -@Entity() -@ObjectType() -export class PowerSnapshot extends BaseEntity { - @Field(_type => ID) - @PrimaryGeneratedColumn() - id: number; - - @Field(_type => Date) - @Column({ - type: 'timestamp without time zone', - transformer: new ColumnDateTransformer(), - }) - @Index({ unique: true }) - time: Date; - - @Field(_type => Int) - @Column('integer', { nullable: true }) - @Index({ unique: true }) - blockNumber?: number; - - @Field() - @Column({ type: 'integer', nullable: true }) - roundNumber: number; - - @Field(_type => Boolean, { nullable: true }) - @Column({ nullable: true }) - @Index() - synced?: boolean; - - @Field(_type => [PowerBoostingSnapshot], { nullable: true }) - @OneToMany( - _type => PowerBoostingSnapshot, - powerBoostingSnapshot => powerBoostingSnapshot.powerSnapshot, - ) - powerBoostingSnapshots?: PowerBoostingSnapshot[]; - - @Field(_type => [PowerBalanceSnapshot], { nullable: true }) - @OneToMany( - _type => PowerBalanceSnapshot, - powerBalanceSnapshot => powerBalanceSnapshot.powerSnapshot, - ) - powerBalanceSnapshots?: PowerBalanceSnapshot[]; -} diff --git a/src/entities/powerSnapshotHistory.ts b/src/entities/powerSnapshotHistory.ts deleted file mode 100644 index 4575555b4..000000000 --- a/src/entities/powerSnapshotHistory.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Field, ID, Int, ObjectType } from 'type-graphql'; -import { Column, Entity, BaseEntity, Index, PrimaryColumn } from 'typeorm'; - -@Entity() -@ObjectType() -export class PowerSnapshotHistory extends BaseEntity { - @Field(_type => ID) - @PrimaryColumn() - id: number; - - @Field(_type => Date) - @Column() - @Index({ unique: true }) - time: Date; - - @Field(_type => Int) - @Column('integer', { nullable: true }) - @Index({ unique: true }) - blockNumber?: number; - - @Field() - @Column({ type: 'integer', nullable: true }) - roundNumber: number; - - @Field(_type => Boolean, { nullable: true }) - @Column({ nullable: true }) - @Index() - synced?: boolean; -} diff --git a/src/entities/previousRoundRank.ts b/src/entities/previousRoundRank.ts deleted file mode 100644 index 5402e846c..000000000 --- a/src/entities/previousRoundRank.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - BaseEntity, - Column, - CreateDateColumn, - Entity, - Index, - ManyToOne, - PrimaryGeneratedColumn, - RelationId, - Unique, - UpdateDateColumn, -} from 'typeorm'; -import { Field, ID, ObjectType } from 'type-graphql'; -import { Project } from './project'; - -@Entity() -@ObjectType() -@Unique(['round', 'project']) -export class PreviousRoundRank extends BaseEntity { - @Field(_type => ID) - @PrimaryGeneratedColumn() - readonly id: number; - - @Index() - @Field(_type => Project) - @ManyToOne(_type => Project) - project: Project; - - @RelationId( - (previousRoundRank: PreviousRoundRank) => previousRoundRank.project, - ) - @Column() - projectId: number; - - @Field() - @Column() - round: number; - - @Field() - @Column() - rank: number; - - @UpdateDateColumn() - updatedAt: Date; - - @CreateDateColumn() - createdAt: Date; -} diff --git a/src/entities/project.ts b/src/entities/project.ts index 83adb224c..8d50c2a50 100644 --- a/src/entities/project.ts +++ b/src/entities/project.ts @@ -33,9 +33,6 @@ import { SocialProfile } from './socialProfile'; import { ProjectVerificationForm } from './projectVerificationForm'; import { ProjectAddress } from './projectAddress'; import { ProjectContacts } from './projectVerificationForm'; -import { ProjectPowerView } from '../views/projectPowerView'; -import { ProjectFuturePowerView } from '../views/projectFuturePowerView'; -import { ProjectInstantPowerView } from '../views/projectInstantPowerView'; import { Category } from './category'; import { FeaturedUpdate } from './featuredUpdate'; import { getHtmlTextSummary } from '../utils/utils'; @@ -72,8 +69,6 @@ export enum SortingField { RecentlyUpdated = 'RecentlyUpdated', Oldest = 'Oldest', QualityScore = 'QualityScore', - GIVPower = 'GIVPower', - InstantBoosting = 'InstantBoosting', ActiveQfRoundRaisedFunds = 'ActiveQfRoundRaisedFunds', EstimatedMatching = 'EstimatedMatching', } @@ -92,7 +87,6 @@ export enum FilterField { AcceptFundOnOptimism = 'acceptFundOnOptimism', AcceptFundOnSolana = 'acceptFundOnSolana', Endaoment = 'fromEndaoment', - BoostedWithGivPower = 'boostedWithGivPower', ActiveQfRound = 'ActiveQfRound', } @@ -110,8 +104,6 @@ export enum OrderField { Donations = 'totalDonations', TraceDonations = 'totalTraceDonations', AcceptGiv = 'givingBlocksId', - GIVPower = 'givPower', - InstantBoosting = 'instantBoosting', } export enum RevokeSteps { @@ -335,27 +327,6 @@ export class Project extends BaseEntity { }) featuredUpdate?: FeaturedUpdate; - @Field(_type => ProjectPowerView, { nullable: true }) - @OneToOne( - _type => ProjectPowerView, - projectPowerView => projectPowerView.project, - ) - projectPower?: ProjectPowerView; - - @Field(_type => ProjectFuturePowerView, { nullable: true }) - @OneToOne( - _type => ProjectFuturePowerView, - projectFuturePowerView => projectFuturePowerView.project, - ) - projectFuturePower?: ProjectFuturePowerView; - - @Field(_type => ProjectInstantPowerView, { nullable: true }) - @OneToOne( - _type => ProjectInstantPowerView, - projectInstantPowerView => projectInstantPowerView.project, - ) - projectInstantPower?: ProjectInstantPowerView; - @Field(_type => String, { nullable: true }) verificationFormStatus?: string; diff --git a/src/entities/user.ts b/src/entities/user.ts index 12ca19950..548e12e36 100644 --- a/src/entities/user.ts +++ b/src/entities/user.ts @@ -16,8 +16,6 @@ import { Reaction } from './reaction'; import { AccountVerification } from './accountVerification'; import { ProjectStatusHistory } from './projectStatusHistory'; import { ProjectVerificationForm } from './projectVerificationForm'; -import { PowerBoosting } from './powerBoosting'; -import { findPowerBoostingsCountByUserId } from '../repositories/powerBoostingRepository'; import { ReferredEvent } from './referredEvent'; import { RecurringDonation } from './recurringDonation'; import { NOTIFICATIONS_EVENT_NAMES } from '../analytics/analytics'; @@ -179,10 +177,6 @@ export class User extends BaseEntity { ) projectStatusHistories?: ProjectStatusHistory[]; - @Field(_type => [PowerBoosting], { nullable: true }) - @OneToMany(_type => PowerBoosting, powerBoosting => powerBoosting.user) - powerBoostings?: PowerBoosting[]; - @UpdateDateColumn() updatedAt: Date; @@ -246,11 +240,6 @@ export class User extends BaseEntity { return likedProjectsCount; } - @Field(_type => Int, { nullable: true }) - async boostedProjectsCount() { - return findPowerBoostingsCountByUserId(this.id); - } - segmentUserId() { return `givethId-${this.id}`; } diff --git a/src/modules.d.ts b/src/modules.d.ts index 45f122f3c..7349ac8ec 100644 --- a/src/modules.d.ts +++ b/src/modules.d.ts @@ -32,8 +32,5 @@ declare namespace NodeJS { OUR_SECRET: string; XDAI_NODE_HTTP_URL: string; TRACE_FILE_UPLOADER_PASSWORD: string; - GIVPOWER_BOOSTING_USER_PROJECTS_LIMIT: string; - GIVPOWER_BOOSTING_PERCENTAGE_PRECISION: string; - GIVPOWER_ROUND_DURATION: string; } } diff --git a/src/repositories/dbCronRepository.test.ts b/src/repositories/dbCronRepository.test.ts deleted file mode 100644 index 2aab73df9..000000000 --- a/src/repositories/dbCronRepository.test.ts +++ /dev/null @@ -1,341 +0,0 @@ -import { assert } from 'chai'; -import { - dropDbCronExtension, - EVERY_MINUTE_CRON_JOB_EXPRESSION, - getTakeSnapshotJobsAndCount, - invokeGivPowerHistoricProcedures, - POWER_BOOSTING_SNAPSHOT_TASK_NAME, - schedulePowerBoostingSnapshot, - setupPgCronExtension, - unSchedulePowerBoostingSnapshot, -} from './dbCronRepository'; -import config from '../config'; -import { - createProjectData, - generateRandomEtheriumAddress, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, - sleep, -} from '../../test/testUtils'; -import { PowerSnapshot } from '../entities/powerSnapshot'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from './powerBoostingRepository'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { setPowerRound } from './powerRoundRepository'; -import { PowerSnapshotHistory } from '../entities/powerSnapshotHistory'; -import { PowerBoostingSnapshotHistory } from '../entities/powerBoostingSnapshotHistory'; -import { PowerBalanceSnapshotHistory } from '../entities/powerBalanceSnapshotHistory'; -import { AppDataSource } from '../orm'; -import { addOrUpdatePowerSnapshotBalances } from './powerBalanceSnapshotRepository'; - -describe( - 'db cron job historic procedures tests cases', - givPowerHistoricTestCases, -); - -function givPowerHistoricTestCases() { - it('should move data older than 1 round less to the historic tables', async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBoosting.clear(); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - await PowerBoostingSnapshotHistory.clear(); - await PowerBalanceSnapshotHistory.clear(); - await PowerSnapshotHistory.clear(); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - - await insertSinglePowerBoosting({ - user: user1, - project: project1, - percentage: 10, - }); - - // Round 1 - await setPowerRound(1); - - await takePowerBoostingSnapshot(); - - const [roundOneSnapshot] = await PowerSnapshot.find({ take: 1 }); - roundOneSnapshot!.roundNumber = 1; - await roundOneSnapshot!.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: roundOneSnapshot!.id, - balance: 20000, - }); - - assert.isDefined(roundOneSnapshot); - - // Round 2 - await setPowerRound(3); - - await takePowerBoostingSnapshot(); - - const roundTwoSnapshot = await PowerSnapshot.findOne({ - where: { - id: roundOneSnapshot!.id + 1, - }, - }); - roundTwoSnapshot!.roundNumber = 5; - await roundTwoSnapshot!.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: roundTwoSnapshot!.id, - balance: 25000, - }); - - // Round 3----------------------------------- - await setPowerRound(3); - - await takePowerBoostingSnapshot(); - - const roundThreeSnapshot = await PowerSnapshot.findOne({ - where: { - id: roundTwoSnapshot!.id + 1, - }, - }); - roundThreeSnapshot!.roundNumber = 5; - await roundThreeSnapshot!.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: roundThreeSnapshot!.id, - balance: 25000, - }); - /// --- end round 3------------------------- - - assert.isDefined(roundThreeSnapshot); - - await invokeGivPowerHistoricProcedures(); - - const [powerSnapshots, powerSnapshotsCount] = - await PowerSnapshot.createQueryBuilder().getManyAndCount(); - const [powerBalanceSnapshots, powerBalanceSnapshotsCount] = - await PowerBalanceSnapshot.createQueryBuilder().getManyAndCount(); - const [powerBoostingSnapshots, powerBoostingSnapshotsCount] = - await PowerBoostingSnapshot.createQueryBuilder().getManyAndCount(); - - // Assert round 2 and 3 remain - assert.equal(powerSnapshotsCount, 2); - assert.equal(powerBalanceSnapshotsCount, 2); - assert.equal(powerBoostingSnapshotsCount, 2); - - // Assert round 1 is not present in main tables - for (const snapshot of powerSnapshots) { - assert.notEqual(snapshot!.roundNumber, roundOneSnapshot?.roundNumber); - } - - for (const balanceSnapshot of powerBalanceSnapshots) { - assert.notEqual(balanceSnapshot!.powerSnapshotId, roundOneSnapshot!.id); - } - - for (const boostingSnapshot of powerBoostingSnapshots) { - assert.notEqual(boostingSnapshot!.powerSnapshotId, roundOneSnapshot!.id); - } - - const [powerSnapshotHistory, powerSnapshotHistoryCount] = - await PowerSnapshotHistory.createQueryBuilder().getManyAndCount(); - const [powerBalanceHistory, powerBalanceHistoryCount] = - await PowerBalanceSnapshotHistory.createQueryBuilder().getManyAndCount(); - const [powerBoostingSnapshotHistory, powerBoostingSnapshotHistoryCount] = - await PowerBoostingSnapshotHistory.createQueryBuilder().getManyAndCount(); - - // only round 1 was inserted, while round 2 and 3 remain in main tables - assert.equal(powerSnapshotHistoryCount, 1); - assert.equal(powerBalanceHistoryCount, 1); - assert.equal(powerBoostingSnapshotHistoryCount, 1); - - assert.equal( - powerSnapshotHistory[0]!.roundNumber, - roundOneSnapshot!.roundNumber, - ); - assert.equal(powerBalanceHistory[0]!.powerSnapshotId, roundOneSnapshot!.id); - assert.equal(powerBalanceHistory[0]!.balance, 20000); - assert.equal( - powerBoostingSnapshotHistory[0]!.powerSnapshotId, - roundOneSnapshot!.id, - ); - }); -} - -describe('db cron job test', () => { - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - it('should set up schedule on bootstrap', async () => { - const enableDbCronJob = - config.get('ENABLE_DB_POWER_BOOSTING_SNAPSHOT') === 'true'; - if (enableDbCronJob) { - const scheduleExpression = config.get( - 'DB_POWER_BOOSTING_SNAPSHOT_CRONJOB_EXPRESSION', - ) as string; - const [jobs, count] = await getTakeSnapshotJobsAndCount(); - assert.isAtLeast(count, 1); - const job = jobs.find( - j => j.jobName === POWER_BOOSTING_SNAPSHOT_TASK_NAME, - ); - assert.isDefined(job); - assert.equal(job?.schedule, scheduleExpression); - await dropDbCronExtension(); - } - }); - - // TODO Should ask Amin what was the purpose of below test case because it doesnt work with other test cases - - // it('should add and remove task', async () => { - // await dropDbCronExtension(); - // await setupPgCronExtension(); - // - // let [, count] = await getTakeSnapshotJobsAndCount(); - // let jobs: CronJob[] = []; - // - // assert.equal(count, 0); - // await schedulePowerBoostingSnapshot(EVERY_YEAR_CRON_JOB_EXPRESSION); - // [jobs, count] = await getTakeSnapshotJobsAndCount(); - // assert.equal(count, 1); - // assert.equal(jobs[0].jobName, POWER_BOOSTING_SNAPSHOT_TASK_NAME); - // assert.equal(jobs[0].schedule, EVERY_YEAR_CRON_JOB_EXPRESSION); - // await unSchedulePowerBoostingSnapshot(); - // [jobs, count] = await getTakeSnapshotJobsAndCount(); - // assert.equal(count, 0); - // }); - - // This test takes one minutes to become complete, just will run it in special cases manually - it.skip('should fill givpower boosting snapshot', async () => { - await PowerBoosting.clear(); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - - await insertSinglePowerBoosting({ - user: user1, - project: project1, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: user1, - project: project2, - percentage: 30, - }); - - await insertSinglePowerBoosting({ - user: user2, - project: project1, - percentage: 30, - }); - await insertSinglePowerBoosting({ - user: user2, - project: project2, - percentage: 40, - }); - await dropDbCronExtension(); - await setupPgCronExtension(); - - await schedulePowerBoostingSnapshot(EVERY_MINUTE_CRON_JOB_EXPRESSION); - - // Wait for one minutes - await sleep(60_000); - - const snapshot = await PowerSnapshot.findOne({ order: { id: 'DESC' } }); - assert.isDefined(snapshot); - - const [powerBoostings, powerBoostingCounts] = - await PowerBoosting.findAndCount({ - select: ['id', 'projectId', 'userId', 'percentage'], - }); - const [powerBoostingSnapshots, powerBoostingSnapshotsCounts] = - await PowerBoostingSnapshot.findAndCount({ - where: { powerSnapshotId: snapshot?.id }, - }); - - assert.equal(powerBoostingSnapshotsCounts, powerBoostingCounts); - powerBoostings.forEach(pb => { - const pbs = powerBoostingSnapshots.find( - p => - p.projectId === pb.projectId && - p.userId === pb.userId && - p.percentage === pb.percentage && - p.powerSnapshotId === snapshot?.id, - ); - assert.isDefined(pbs); - }); - - await unSchedulePowerBoostingSnapshot(); - }); - - // This test takes one minutes to become complete, just will run it in special cases manually - it.skip('should fill givpower boosting snapshot, not include non-verified project power boostings', async () => { - await PowerBoosting.clear(); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb({ - ...createProjectData(), - verified: false, - }); - - await insertSinglePowerBoosting({ - user: user1, - project: project1, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: user1, - project: project2, - percentage: 30, - }); - await insertSinglePowerBoosting({ - user: user1, - project: project3, - percentage: 20, - }); - await insertSinglePowerBoosting({ - user: user2, - project: project1, - percentage: 30, - }); - await insertSinglePowerBoosting({ - user: user2, - project: project2, - percentage: 40, - }); - await dropDbCronExtension(); - await setupPgCronExtension(); - - await schedulePowerBoostingSnapshot(EVERY_MINUTE_CRON_JOB_EXPRESSION); - - // Wait for one minutes - await sleep(60_000); - - const snapshot = await PowerSnapshot.findOne({ order: { id: 'DESC' } }); - assert.isDefined(snapshot); - - const powerBoostingSnapshotsCounts = await PowerBoostingSnapshot.count({ - where: { powerSnapshotId: snapshot?.id }, - }); - - // There is 5 power boosting but one of them is for a non verified project - assert.equal(powerBoostingSnapshotsCounts, 4); - - await unSchedulePowerBoostingSnapshot(); - }); -}); diff --git a/src/repositories/dbCronRepository.ts b/src/repositories/dbCronRepository.ts index b000a3a2d..0c63d958b 100644 --- a/src/repositories/dbCronRepository.ts +++ b/src/repositories/dbCronRepository.ts @@ -1,108 +1,5 @@ -import { CronJob } from '../entities/CronJob'; import { logger } from '../utils/logger'; -import { AppDataSource, CronDataSource } from '../orm'; - -export const POWER_BOOSTING_SNAPSHOT_TASK_NAME = - 'take givpower boosting snapshot'; -export const ARCHIVE_POWER_SNAPSHOTS_TASK_NAME = 'archive givpower snapshots'; - -export const EVERY_MINUTE_CRON_JOB_EXPRESSION = '* * * * * *'; -export const EVERY_YEAR_CRON_JOB_EXPRESSION = '0 0 1 1 *'; - -export const setupPgCronExtension = async () => { - try { - await CronDataSource.getDataSource().query(` - CREATE EXTENSION IF NOT EXISTS PG_CRON; - - GRANT USAGE ON SCHEMA CRON TO POSTGRES; - `); - } catch (e) { - logger.error('setupPgCronExtension() error', e); - } -}; - -export const schedulePowerBoostingSnapshot = async ( - cronJobExpression: string, -) => { - try { - await CronDataSource.getDataSource().query(` - CREATE EXTENSION IF NOT EXISTS PG_CRON; - - GRANT USAGE ON SCHEMA CRON TO POSTGRES; - - SELECT CRON.SCHEDULE( - '${POWER_BOOSTING_SNAPSHOT_TASK_NAME}', - '${cronJobExpression}', - $$CALL public."TAKE_POWER_BOOSTING_SNAPSHOT"()$$ - ); - `); - } catch (e) { - logger.error('schedulePowerBoostingSnapshot() error', e); - } -}; - -export const invokeGivPowerHistoricProcedures = async () => { - try { - const queryRunner = AppDataSource.getDataSource().createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - await queryRunner.query(` - CALL public."ARCHIVE_POWER_BOOSTING_OLD_SNAPSHOT_DATA"() - `); - await queryRunner.commitTransaction(); - } catch (e) { - logger.error('invokeGivPowerHistoricProcedures() error', e); - await queryRunner.rollbackTransaction(); - } finally { - await queryRunner.release(); - } - } catch (e) { - logger.error('invokeGivPowerHistoricProcedures() error', e); - } -}; - -export const schedulePowerSnapshotsHistory = async ( - cronJobExpression: string, -) => { - try { - await CronDataSource.getDataSource().query(` - CREATE EXTENSION IF NOT EXISTS PG_CRON; - - GRANT USAGE ON SCHEMA CRON TO POSTGRES; - - SELECT CRON.SCHEDULE( - '${ARCHIVE_POWER_SNAPSHOTS_TASK_NAME}', - '${cronJobExpression}', - $$CALL public."ARCHIVE_POWER_BOOSTING_OLD_SNAPSHOT_DATA"()$$ - ); - `); - } catch (e) { - logger.error('schedulePowerSnapshotsHistory()', e); - } -}; - -export const unSchedulePowerBoostingSnapshot = async () => { - try { - await CronDataSource.getDataSource().query( - `SELECT cron.unschedule('${POWER_BOOSTING_SNAPSHOT_TASK_NAME}')`, - ); - } catch (e) { - logger.error('unSchedulePowerBoostingSnapshot() error', e); - } -}; - -export const getTakeSnapshotJobsAndCount = async (): Promise< - [CronJob[], number] -> => { - return await CronDataSource.getDataSource() - .createQueryBuilder(CronJob, 'job') - .where('job.jobname = :jobName', { - jobName: POWER_BOOSTING_SNAPSHOT_TASK_NAME, - }) - .getManyAndCount(); -}; +import { CronDataSource } from '../orm'; export const dropDbCronExtension = async () => { try { diff --git a/src/repositories/instantBoostingRepository.test.ts b/src/repositories/instantBoostingRepository.test.ts deleted file mode 100644 index b1c62d2f1..000000000 --- a/src/repositories/instantBoostingRepository.test.ts +++ /dev/null @@ -1,294 +0,0 @@ -import { assert } from 'chai'; -import { - getMaxFetchedUpdatedAtTimestamp, - // getLastInstantPowerUpdatedAt, - getUsersBoostedWithoutInstanceBalance, - refreshProjectInstantPowerView, - saveOrUpdateInstantPowerBalances, - setMaxFetchedUpdatedAtTimestamp, -} from './instantBoostingRepository'; -import { InstantPowerBalance } from '../entities/instantPowerBalance'; -import { - createProjectData, - generateRandomEtheriumAddress, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, -} from '../../test/testUtils'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { insertSinglePowerBoosting } from './powerBoostingRepository'; -import { InstantPowerFetchState } from '../entities/instantPowerFetchState'; -import { ProjectInstantPowerView } from '../views/projectInstantPowerView'; - -// describe( -// 'getLastInstantPowerUpdatedAt test cases', -// getLastInstantPowerUpdatedAtTestCases, -// ); -describe( - 'saveOrUpdateInstantPowerBalances test cases', - saveOrUpdateInstantPowerBalancesTestCases, -); -describe( - 'getUsersBoostedWithoutInstanceBalance test cases', - getUsersBoostedWithoutInstanceBalanceTestCases, -); -describe( - 'instance boosting latest synced block test cases', - latestSyncedBlockTestCases, -); -describe( - 'projectInstantPowerView test cases', - projectInstantPowerViewTestCases, -); -// function getLastInstantPowerUpdatedAtTestCases() { -// beforeEach(async () => { -// await InstantPowerBalance.clear(); -// }); -// it('should return zero for empty table', async () => { -// const maxUpdateAt = await getLastInstantPowerUpdatedAt(); -// assert.equal(maxUpdateAt, 0); -// }); -// -// it('should return correct last chain update at', async () => { -// const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); -// const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); -// -// await saveOrUpdateInstantPowerBalances([ -// { userId: user1.id, balanceAggregatorUpdatedAt: 100, balance: 1000 }, -// { userId: user2.id, balanceAggregatorUpdatedAt: 300, balance: 3000 }, -// ]); -// -// let maxUpdateAt = await getLastInstantPowerUpdatedAt(); -// assert.equal(maxUpdateAt, 300); -// -// await saveOrUpdateInstantPowerBalances([ -// { userId: user1.id, balanceAggregatorUpdatedAt: 400, balance: 900 }, -// ]); -// -// maxUpdateAt = await getLastInstantPowerUpdatedAt(); -// assert.equal(maxUpdateAt, 400); -// }); -// } - -function saveOrUpdateInstantPowerBalancesTestCases() { - beforeEach(async () => { - await InstantPowerBalance.clear(); - }); - - it('should create multiple entities in one call', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const entities: Partial[] = [ - { - userId: user1.id, - balanceAggregatorUpdatedAt: new Date(100_000), - balance: 1000, - }, - { - userId: user2.id, - balanceAggregatorUpdatedAt: new Date(300_000), - balance: 3000, - }, - ]; - - await saveOrUpdateInstantPowerBalances(entities); - - const instances = await InstantPowerBalance.find({ - order: { userId: 'ASC' }, - }); - - assert.includeDeepMembers(instances, entities); - }); - - it('should update entities', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const entities: Partial[] = [ - { - userId: user1.id, - balanceAggregatorUpdatedAt: new Date(100_000), - balance: 1000, - }, - { - userId: user2.id, - balanceAggregatorUpdatedAt: new Date(300_000), - balance: 3000, - }, - ]; - - await saveOrUpdateInstantPowerBalances(entities); - entities[0].balanceAggregatorUpdatedAt = new Date(200_000); - entities[0].balance = 2000; - - entities[1].balanceAggregatorUpdatedAt = new Date(400_000); - entities[1].balance = 4000; - - await saveOrUpdateInstantPowerBalances(entities); - const instances = await InstantPowerBalance.find({ - order: { userId: 'ASC' }, - }); - - assert.includeDeepMembers(instances, entities); - }); - - it('should create and update entities together', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user3 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const entities: Partial[] = [ - { - userId: user1.id, - balanceAggregatorUpdatedAt: new Date(100_000), - balance: 1000, - }, - { - userId: user2.id, - balanceAggregatorUpdatedAt: new Date(300_000), - balance: 3000, - }, - ]; - - await saveOrUpdateInstantPowerBalances(entities); - - entities[1].balanceAggregatorUpdatedAt = new Date(400_000); - entities[1].balance = 4000; - - entities.push({ - userId: user3.id, - balanceAggregatorUpdatedAt: new Date(500_000), - balance: 5000, - }); - - await saveOrUpdateInstantPowerBalances(entities); - const instances = await InstantPowerBalance.find({ - order: { userId: 'ASC' }, - }); - - assert.includeDeepMembers(instances, entities); - }); -} - -function getUsersBoostedWithoutInstanceBalanceTestCases() { - beforeEach(async () => { - await PowerBoosting.clear(); - }); - it('should return users have boosted without instance balance', async () => { - const project = await saveProjectDirectlyToDb(createProjectData()); - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - await saveUserDirectlyToDb(generateRandomEtheriumAddress()); // User3 - - await insertSinglePowerBoosting({ - user: user1, - project, - percentage: 100, - }); - await insertSinglePowerBoosting({ - user: user2, - project, - percentage: 100, - }); - let result = await getUsersBoostedWithoutInstanceBalance(); - assert.deepEqual(result, [ - { id: user1.id, walletAddress: user1.walletAddress }, - { id: user2.id, walletAddress: user2.walletAddress }, - ]); - - await saveOrUpdateInstantPowerBalances([ - { - userId: user1.id, - balanceAggregatorUpdatedAt: new Date(1_000_000), - balance: 1000, - }, - ]); - - // Save balance for a user, should not have user 1 data in the result anymore - result = await getUsersBoostedWithoutInstanceBalance(); - assert.deepEqual(result, [ - { id: user2.id, walletAddress: user2.walletAddress }, - ]); - }); -} - -function latestSyncedBlockTestCases() { - beforeEach(async () => { - await InstantPowerFetchState.clear(); - }); - it('should return 0 for empty table', async () => { - const result = await getMaxFetchedUpdatedAtTimestamp(); - assert.equal(result, 0); - }); - - it('should return correct latest synced block', async () => { - await setMaxFetchedUpdatedAtTimestamp(1000); - const result = await getMaxFetchedUpdatedAtTimestamp(); - assert.equal(result, 1000); - - await setMaxFetchedUpdatedAtTimestamp(2000); - const result2 = await getMaxFetchedUpdatedAtTimestamp(); - assert.equal(result2, 2000); - }); -} - -function projectInstantPowerViewTestCases() { - beforeEach(async () => { - await InstantPowerBalance.clear(); - await PowerBoosting.clear(); - }); - - it('should return correct order and values of projects instant power', async () => { - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - await saveProjectDirectlyToDb(createProjectData()); - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - await saveUserDirectlyToDb(generateRandomEtheriumAddress()); // User3 - - await insertSinglePowerBoosting({ - user: user1, - project: project1, - percentage: 100, - }); - await insertSinglePowerBoosting({ - user: user2, - project: project2, - percentage: 100, - }); - - await saveOrUpdateInstantPowerBalances([ - { - userId: user1.id, - balanceAggregatorUpdatedAt: new Date(1_000_000), - balance: 1000, - }, - { - userId: user2.id, - balanceAggregatorUpdatedAt: new Date(2_000_000), - balance: 2000, - }, - ]); - - await refreshProjectInstantPowerView(); - const result = await ProjectInstantPowerView.find({ - order: { totalPower: 'DESC' }, - select: ['projectId', 'totalPower', 'powerRank'], - }); - - assert.isAtLeast(result.length, 3); - assert.include(result[0], { - projectId: project2.id, - totalPower: 2000, - powerRank: '1', - }); - assert.include(result[1], { - projectId: project1.id, - totalPower: 1000, - powerRank: '2', - }); - - // The rest of projects must have 0 power and rank 3 - assert.include(result[2], { - totalPower: 0, - powerRank: '3', - }); - }); -} diff --git a/src/repositories/instantBoostingRepository.ts b/src/repositories/instantBoostingRepository.ts deleted file mode 100644 index 1e7e3535f..000000000 --- a/src/repositories/instantBoostingRepository.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { InstantPowerBalance } from '../entities/instantPowerBalance'; -import { logger } from '../utils/logger'; -import { AppDataSource } from '../orm'; -import { InstantPowerFetchState } from '../entities/instantPowerFetchState'; -import { ProjectUserInstantPowerView } from '../views/projectUserInstantPowerView'; - -export const saveOrUpdateInstantPowerBalances = async ( - instances: Partial[], -): Promise => { - try { - logger.debug( - 'saveOrUpdateInstantPowerBalances ', - JSON.stringify({ instances }, null, 2), - ); - - const userIdInstanceDic = {}; - instances.forEach(instance => { - if (!instance.userId) { - throw new Error('userId is required for InstantPowerBalance'); - } - userIdInstanceDic[instance.userId!] = instance; - }); - - await InstantPowerBalance.createQueryBuilder() - .insert() - .into(InstantPowerBalance) - .values(Object.values(userIdInstanceDic)) - .orUpdate(['balance', 'balanceAggregatorUpdatedAt'], ['userId']) - .execute(); - } catch (e) { - logger.error('saveOrUpdateInstantPowerBalances error', e); - throw e; - } -}; - -export const getUsersBoostedWithoutInstanceBalance = async ( - limit = 50, - offset = 0, -): Promise<{ id: number; walletAddress: string }[]> => { - logger.debug('getUsersBoostedWithoutBalance', { limit, offset }); - return await AppDataSource.getDataSource().query( - ` - SELECT ID, "walletAddress" FROM PUBLIC.USER - INNER JOIN - (SELECT "userId" - FROM POWER_BOOSTING AS "boosting" - WHERE NOT EXISTS - (SELECT - FROM INSTANT_POWER_BALANCE AS "balance" - WHERE BALANCE."userId" = BOOSTING."userId" ) ) - WITHOUT_BALANCE_USERS - ON ID = WITHOUT_BALANCE_USERS."userId" AND "walletAddress" IS NOT NULL - ORDER BY "id" ASC - LIMIT $1 - OFFSET $2 - `, - [limit, offset], - ); -}; - -/** - * Sets the latest block number that instant power balances were fetched from subgraph - * @param blockInfo {BlockInfo} block number and timestamp - */ -export const setMaxFetchedUpdatedAtTimestamp = async ( - timestampMs: number, -): Promise => { - let state = await InstantPowerFetchState.findOne({ where: {} }); - - if (!state) { - state = InstantPowerFetchState.create({ - id: true, - maxFetchedUpdateAtTimestampMS: timestampMs, - }); - } else { - state.maxFetchedUpdateAtTimestampMS = timestampMs; - } - return state.save(); -}; - -/** - * Returns the latest block number that instant power balances were fetched from subgraph - * @returns {Promise} - */ -export const getMaxFetchedUpdatedAtTimestamp = async (): Promise => { - const state = await InstantPowerFetchState.findOne({ where: {} }); - - return state?.maxFetchedUpdateAtTimestampMS || 0; -}; - -export const refreshProjectInstantPowerView = async (): Promise => { - logger.debug('Refresh project_instant_power_view materialized view'); - try { - return AppDataSource.getDataSource().query( - ` - REFRESH MATERIALIZED VIEW CONCURRENTLY project_instant_power_view - `, - ); - } catch (e) { - logger.error('refreshProjectInstantPowerView() error', e); - } -}; - -export const refreshProjectUserInstantPowerView = async (): Promise => { - logger.debug('Refresh project_user_instant_power_view materialized view'); - try { - return AppDataSource.getDataSource().query( - ` - REFRESH MATERIALIZED VIEW CONCURRENTLY project_user_instant_power_view - `, - ); - } catch (e) { - logger.error('refreshProjectUserInstantPowerView() error', e); - } -}; - -export const getProjectUserInstantPowerView = async ( - projectId: number, - limit = 50, - offset = 0, -): Promise<[ProjectUserInstantPowerView[], number]> => { - return ProjectUserInstantPowerView.createQueryBuilder( - 'projectUserInstantPowerView', - ) - .where('projectUserInstantPowerView.projectId = :projectId', { projectId }) - .take(limit) - .skip(offset) - .orderBy('projectUserInstantPowerView.boostedPower', 'DESC') - .leftJoin('projectUserInstantPowerView.user', 'user') - .select([ - 'projectUserInstantPowerView.id', - 'projectUserInstantPowerView.projectId', - 'projectUserInstantPowerView.userId', - 'projectUserInstantPowerView.boostedPower', - 'user.walletAddress', - 'user.name', - 'user.avatar', - ]) - .getManyAndCount(); -}; diff --git a/src/repositories/powerBalanceSnapshotRepository.test.ts b/src/repositories/powerBalanceSnapshotRepository.test.ts deleted file mode 100644 index 37123e64f..000000000 --- a/src/repositories/powerBalanceSnapshotRepository.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - assertNotThrowsAsync, - generateRandomEtheriumAddress, - saveUserDirectlyToDb, -} from '../../test/testUtils'; -import { PowerSnapshot } from '../entities/powerSnapshot'; -import { addOrUpdatePowerSnapshotBalances } from './powerBalanceSnapshotRepository'; - -describe( - 'createPowerSnapshotBalances test cases', - createPowerSnapshotBalancesTestCases, -); - -function createPowerSnapshotBalancesTestCases() { - it('should create power snapshot balance', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - let powerSnapshotTime = user.id * 1000; - - const powerSnapshot = await PowerSnapshot.create({ - time: new Date(powerSnapshotTime++), - blockNumber: powerSnapshotTime, - }).save(); - - await assertNotThrowsAsync(async () => { - await addOrUpdatePowerSnapshotBalances({ - powerSnapshotId: powerSnapshot.id, - userId: user.id, - balance: 100, - }); - }); - }); -} diff --git a/src/repositories/powerBalanceSnapshotRepository.ts b/src/repositories/powerBalanceSnapshotRepository.ts deleted file mode 100644 index 58e70107e..000000000 --- a/src/repositories/powerBalanceSnapshotRepository.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; - -type PowerBalanceSnapshotParams = Pick< - PowerBalanceSnapshot, - 'userId' | 'balance' | 'powerSnapshotId' ->; -export const addOrUpdatePowerSnapshotBalances = async ( - params: PowerBalanceSnapshotParams[] | PowerBalanceSnapshotParams, -): Promise => { - await PowerBalanceSnapshot.createQueryBuilder() - .insert() - .into(PowerBalanceSnapshot) - .values(params) - .orUpdate(['balance'], ['userId', 'powerSnapshotId']) - .execute(); -}; - -export const findCurrentPowerBalanceByUserId = async ( - userId: number, -): Promise => { - const balance = await PowerBalanceSnapshot.createQueryBuilder('powerBalance') - .select('powerBalance.balance AS "givPower"') - .where('powerBalance.userId = :userId', { userId }) - .orderBy('powerBalance.id', 'DESC') - .limit(1) - .getRawOne(); - - return balance?.givPower || 0; -}; - -export const findPowerBalances = async ( - round?: number, - userIds: number[] = [], - powerSnapshotIds: number[] = [], - take: number = 100, - skip: number = 0, -): Promise<[PowerBalanceSnapshot[], number]> => { - const query = PowerBalanceSnapshot.createQueryBuilder( - 'powerBalance', - ).innerJoin('powerBalance.powerSnapshot', 'powerSnapshot'); - - if (round) { - query.where('powerSnapshot.roundNumber = :round', { round }); - } - - if (userIds.length > 0 && powerSnapshotIds.length === 0) { - query.where('powerBalance.userId IN (:...userIds)', { userIds }); - } else if (userIds.length === 0 && powerSnapshotIds.length > 0) { - query.where('powerBalance.powerSnapshotId IN (:...powerSnapshotIds)', { - powerSnapshotIds, - }); - } else if (userIds.length > 0 && powerSnapshotIds.length > 0) { - query - .where('powerBalance.userId IN (:...userIds)', { userIds }) - .andWhere('powerBalance.powerSnapshotId IN (:...powerSnapshotIds)', { - powerSnapshotIds, - }); - } - - return query - .orderBy('powerBalance.id', 'DESC') - .take(take) - .skip(skip) - .getManyAndCount(); -}; diff --git a/src/repositories/powerBoostingRepository.test.ts b/src/repositories/powerBoostingRepository.test.ts deleted file mode 100644 index 1fcb39afc..000000000 --- a/src/repositories/powerBoostingRepository.test.ts +++ /dev/null @@ -1,1005 +0,0 @@ -import { assert } from 'chai'; -import { - assertThrowsAsync, - createProjectData, - generateRandomEtheriumAddress, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, -} from '../../test/testUtils'; -import { - cancelProjectBoosting, - findPowerBoostings, - findUserPowerBoosting, - getPowerBoostingSnapshotRound, - insertSinglePowerBoosting, - setMultipleBoosting, - setSingleBoosting, - takePowerBoostingSnapshot, -} from './powerBoostingRepository'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { PowerSnapshot } from '../entities/powerSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { errorMessages } from '../utils/errorMessages'; -import { AppDataSource } from '../orm'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; - -describe('findUserPowerBoosting() testCases', findUserPowerBoostingTestCases); -describe('findPowerBoostings() testCases', findPowerBoostingsTestCases); -describe('setMultipleBoosting() testCases', setMultipleBoostingTestCases); -describe('setSingleBoosting() testCases', setSingleBoostingTestCases); -describe('power boosting snapshot testCases', powerBoostingSnapshotTests); - -function findUserPowerBoostingTestCases() { - it('should return all non-zero power boostings', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const firstUserPowerBoostings = await findUserPowerBoosting(firstUser.id); - assert.equal(firstUserPowerBoostings.length, 2); - firstUserPowerBoostings.forEach(powerBoosting => { - assert.equal(powerBoosting.user.id, firstUser.id); - }); - }); -} - -function findPowerBoostingsTestCases() { - it('should return all power boostings, filter by projectId', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 20, - skip: 1, - orderBy: { - field: 'updatedAt', - direction: 'DESC', - }, - projectId: firstProject.id, - }); - assert.equal(totalCount, 2); - powerBoostings.forEach(powerBoosting => { - assert.equal(powerBoosting.project.id, firstProject.id); - }); - }); - it('should return all power boostings, filter by userId', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 20, - skip: 1, - orderBy: { - field: 'updatedAt', - direction: 'DESC', - }, - userId: firstUser.id, - }); - assert.equal(totalCount, 2); - powerBoostings.forEach(powerBoosting => { - assert.equal(powerBoosting.user.id, firstUser.id); - }); - }); - it('should return all power boostings, filter by userId and projectId', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 20, - skip: 1, - orderBy: { - field: 'updatedAt', - direction: 'DESC', - }, - userId: firstUser.id, - projectId: firstProject.id, - }); - assert.equal(totalCount, 1); - powerBoostings.forEach(powerBoosting => { - assert.equal(powerBoosting.user.id, firstUser.id); - assert.equal(powerBoosting.project.id, firstProject.id); - }); - }); - it('should return all power boostings, filter by projectId, with sending take', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - const secondPower = await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 1, - skip: 0, - orderBy: { - field: 'updatedAt', - direction: 'DESC', - }, - projectId: firstProject.id, - }); - assert.equal(totalCount, 2); - assert.equal(powerBoostings.length, 1); - powerBoostings.forEach(powerBoosting => { - assert.equal(powerBoosting.id, secondPower.id); - }); - }); - it('should return all power boostings, filter by projectId, with sending skip', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const firstPower = await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 1, - skip: 1, - orderBy: { - field: 'updatedAt', - direction: 'DESC', - }, - projectId: firstProject.id, - }); - assert.equal(totalCount, 2); - assert.equal(powerBoostings.length, 1); - assert.equal(powerBoostings[0].id, firstPower.id); - }); - it('should return all power boostings, filter by projectId,order by updateAt ASC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 20, - skip: 0, - orderBy: { - field: 'updatedAt', - direction: 'ASC', - }, - projectId: firstProject.id, - }); - assert.equal(totalCount, 2); - assert.isTrue(powerBoostings[0].updatedAt < powerBoostings[1].updatedAt); - }); - it('should return all power boostings, filter by projectId,order by updateAt DESC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 20, - skip: 0, - orderBy: { - field: 'updatedAt', - direction: 'DESC', - }, - projectId: firstProject.id, - }); - assert.equal(totalCount, 2); - assert.isTrue(powerBoostings[0].updatedAt > powerBoostings[1].updatedAt); - }); - it('should return all power boostings, filter by projectId,order by createdAt ASC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 20, - skip: 0, - orderBy: { - field: 'createdAt', - direction: 'ASC', - }, - projectId: firstProject.id, - }); - assert.equal(totalCount, 2); - assert.isTrue(powerBoostings[0].createdAt < powerBoostings[1].createdAt); - }); - it('should return all power boostings, filter by projectId,order by createdAt DESC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 20, - skip: 0, - orderBy: { - field: 'createdAt', - direction: 'DESC', - }, - projectId: firstProject.id, - }); - assert.equal(totalCount, 2); - assert.isTrue(powerBoostings[0].createdAt > powerBoostings[1].createdAt); - }); - - it('should return all power boostings, filter by projectId,order by percentage ASC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 20, - skip: 0, - orderBy: { - field: 'percentage', - direction: 'ASC', - }, - projectId: firstProject.id, - }); - assert.equal(totalCount, 2); - assert.isTrue(powerBoostings[0].percentage < powerBoostings[1].percentage); - }); - it('should return all power boostings, filter by projectId,order by percentage DESC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 4, - }); - const [powerBoostings, totalCount] = await findPowerBoostings({ - take: 20, - skip: 0, - orderBy: { - field: 'percentage', - direction: 'DESC', - }, - projectId: firstProject.id, - }); - assert.equal(totalCount, 2); - assert.isTrue(powerBoostings[0].percentage > powerBoostings[1].percentage); - }); -} - -function setMultipleBoostingTestCases() { - it('should set multiple boosting for one user', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - const userBoostings = await setMultipleBoosting({ - userId: user.id, - projectIds: [firstProject.id, secondProject.id, thirdProject.id], - percentages: [40, 20, 40], - }); - assert.equal(userBoostings.length, 3); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === firstProject.id && - powerBoosting.percentage === 40, - ), - ); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === secondProject.id && - powerBoosting.percentage === 20, - ), - ); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === thirdProject.id && - powerBoosting.percentage === 40, - ), - ); - }); - it('should set multiple boosting for one user when js summation shows a little greater number', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - const fourthProject = await saveProjectDirectlyToDb(createProjectData()); - const fifthProject = await saveProjectDirectlyToDb(createProjectData()); - const userBoostings = await setMultipleBoosting({ - userId: user.id, - projectIds: [ - firstProject.id, - secondProject.id, - thirdProject.id, - fourthProject.id, - fifthProject.id, - ], - percentages: [50.46, 18, 12.62, 9.46, 9.46], - }); - assert.equal(userBoostings.length, 5); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === firstProject.id && - powerBoosting.percentage === 50.46, - ), - ); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === secondProject.id && - powerBoosting.percentage === 18, - ), - ); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === thirdProject.id && - powerBoosting.percentage === 12.62, - ), - ); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === fourthProject.id && - powerBoosting.percentage === 9.46, - ), - ); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === fifthProject.id && - powerBoosting.percentage === 9.46, - ), - ); - }); - it('should set multiple boosting for one user and remove previous boostings', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - const fourthProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user, - project: fourthProject, - percentage: 10, - }); - - // This would remove previous power boostings - const userBoostings = await setMultipleBoosting({ - userId: user.id, - projectIds: [firstProject.id, secondProject.id, thirdProject.id], - percentages: [10, 10, 80], - }); - assert.equal(userBoostings.length, 3); - assert.isNotOk( - userBoostings.find( - powerBoosting => powerBoosting.project.id === fourthProject.id, - ), - ); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === firstProject.id && - powerBoosting.percentage === 10, - ), - ); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === secondProject.id && - powerBoosting.percentage === 10, - ), - ); - assert.isOk( - userBoostings.find( - powerBoosting => - powerBoosting.project.id === thirdProject.id && - powerBoosting.percentage === 80, - ), - ); - }); -} - -function setSingleBoostingTestCases() { - it('should set single boosting with 100%, when boosted multi project', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - await setMultipleBoosting({ - userId: user.id, - projectIds: [firstProject.id, secondProject.id, thirdProject.id], - percentages: [40, 20, 40], - }); - await setSingleBoosting({ - userId: user.id, - projectId: firstProject.id, - percentage: 100, - }); - const [userPowerBoostings] = await findPowerBoostings({ - userId: user.id, - orderBy: { - field: 'updatedAt', - direction: 'ASC', - }, - }); - assert.equal(userPowerBoostings.length, 1); - assert.equal(userPowerBoostings[0].percentage, 100); - assert.equal(userPowerBoostings[0].projectId, firstProject.id); - }); - it('should set single boosting with 100%', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - - await setSingleBoosting({ - userId: user.id, - projectId: firstProject.id, - percentage: 100, - }); - - const [userPowerBoostings] = await findPowerBoostings({ - userId: user.id, - orderBy: { - field: 'updatedAt', - direction: 'ASC', - }, - }); - - assert.equal(userPowerBoostings.length, 1); - assert.equal(userPowerBoostings[0].percentage, 100); - assert.equal(userPowerBoostings[0].projectId, firstProject.id); - }); - - it('should set single boosting with 0%, when boosted multi project', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - await setMultipleBoosting({ - userId: user.id, - projectIds: [firstProject.id, secondProject.id, thirdProject.id], - percentages: [20, 40, 40], - }); - - await setSingleBoosting({ - userId: user.id, - projectId: firstProject.id, - percentage: 0, - }); - const [userPowerBoostings] = await findPowerBoostings({ - userId: user.id, - orderBy: { - field: 'updatedAt', - direction: 'ASC', - }, - }); - - assert.equal(userPowerBoostings.length, 2); - - assert.isOk( - userPowerBoostings.find( - powerBoosting => - powerBoosting.project.id === secondProject.id && - powerBoosting.percentage === 50, - ), - ); - assert.isOk( - userPowerBoostings.find( - powerBoosting => - powerBoosting.project.id === thirdProject.id && - powerBoosting.percentage === 50, - ), - ); - }); - it('should set single boosting with 0%', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - - await setSingleBoosting({ - userId: user.id, - projectId: firstProject.id, - percentage: 100, - }); - - await cancelProjectBoosting({ - userId: user.id, - projectId: firstProject.id, - }); - const [userPowerBoostings] = await findPowerBoostings({ - userId: user.id, - orderBy: { - field: 'updatedAt', - direction: 'ASC', - }, - }); - - assert.equal(userPowerBoostings.length, 0); - }); - it('should get error when set single boosting with something between 0-100', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - await assertThrowsAsync(async () => { - await setSingleBoosting({ - userId: user.id, - projectId: firstProject.id, - percentage: 70, - }); - }, errorMessages.ERROR_GIVPOWER_BOOSTING_FIRST_PROJECT_100_PERCENT); - }); -} - -function powerBoostingSnapshotTests() { - beforeEach(async () => { - await PowerBoosting.clear(); - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - }); - it('should take snapshot of power boosting and create power balance snapshot', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - - await insertSinglePowerBoosting({ - user: user1, - project: project1, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: user1, - project: project2, - percentage: 30, - }); - await insertSinglePowerBoosting({ - user: user2, - project: project1, - percentage: 30, - }); - await insertSinglePowerBoosting({ - user: user2, - project: project2, - percentage: 40, - }); - - await takePowerBoostingSnapshot(); - - const [snapshot] = await PowerSnapshot.find({ take: 1 }); - assert.isDefined(snapshot); - - const [powerBoostings, powerBoostingCounts] = - await PowerBoosting.findAndCount({ - take: 4, - select: ['id', 'projectId', 'userId', 'percentage'], - }); - const [powerBoostingSnapshots, powerBoostingSnapshotsCounts] = - await PowerBoostingSnapshot.findAndCount({ - where: { powerSnapshotId: snapshot?.id }, - take: 4, - }); - - const [powerBalanceSnapshots, powerBalanceSnapshotsCounts] = - await PowerBalanceSnapshot.findAndCount({ - where: { powerSnapshotId: snapshot?.id }, - }); - - assert.equal(powerBoostingCounts, powerBoostingSnapshotsCounts); - powerBoostings.forEach(pb => { - const pbs = powerBoostingSnapshots.find( - p => - p.projectId === pb.projectId && - p.userId === pb.userId && - p.percentage === pb.percentage && - p.powerSnapshotId === snapshot?.id, - ); - assert.isDefined(pbs); - }); - - assert.equal(powerBalanceSnapshotsCounts, 2); - assert.isDefined( - powerBalanceSnapshots.find( - p => - p.userId === user1.id && - p.powerSnapshotId === snapshot?.id && - p.balance === null, - ), - ); - assert.isDefined( - powerBalanceSnapshots.find( - p => - p.userId === user2.id && - p.powerSnapshotId === snapshot?.id && - p.balance === null, - ), - ); - }); - - it('should take snapshots for only verified projects', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user3 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - // Project 3 is unverified - const project3 = await saveProjectDirectlyToDb({ - ...createProjectData(), - verified: false, - }); - - await insertSinglePowerBoosting({ - user: user1, - project: project1, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: user2, - project: project2, - percentage: 20, - }); - - await insertSinglePowerBoosting({ - user: user1, - project: project3, - percentage: 30, - }); - - await insertSinglePowerBoosting({ - user: user2, - project: project3, - percentage: 40, - }); - - await insertSinglePowerBoosting({ - user: user3, - project: project3, - percentage: 50, - }); - - await takePowerBoostingSnapshot(); - - const [snapshot] = await PowerSnapshot.find({ take: 1 }); - assert.isDefined(snapshot); - - const [powerBoostings] = await PowerBoosting.findAndCount({ - select: ['id', 'projectId', 'userId', 'percentage'], - }); - const [powerBoostingSnapshots, powerBoostingSnapshotsCounts] = - await PowerBoostingSnapshot.findAndCount({ - where: { powerSnapshotId: snapshot?.id }, - }); - - const [powerBalanceSnapshots, powerBalanceSnapshotsCounts] = - await PowerBalanceSnapshot.findAndCount({ - where: { powerSnapshotId: snapshot?.id }, - }); - - assert.equal(powerBoostingSnapshotsCounts, 2); // User1 -> Project1 and User2 -> Project2 - powerBoostings.forEach(pb => { - const pbs = powerBoostingSnapshots.find( - p => - p.projectId === pb.projectId && - p.userId === pb.userId && - p.percentage === pb.percentage && - p.powerSnapshotId === snapshot?.id, - ); - if (pb.project.verified) assert.isDefined(pbs); - else assert.isUndefined(pbs); - }); - - assert.equal(powerBalanceSnapshotsCounts, 2); - assert.isDefined( - powerBalanceSnapshots.find( - p => - p.userId === user1.id && - p.powerSnapshotId === snapshot?.id && - p.balance === null, - ), - ); - assert.isDefined( - powerBalanceSnapshots.find( - p => - p.userId === user2.id && - p.powerSnapshotId === snapshot?.id && - p.balance === null, - ), - ); - }); - - it('should return snapshot corresponding round correctly', async () => { - await takePowerBoostingSnapshot(); - - let [snapshot] = (await PowerSnapshot.find({ take: 1 })) as PowerSnapshot[]; - assert.isDefined(snapshot); - - const round = getPowerBoostingSnapshotRound(snapshot as PowerSnapshot); - - const firstGivbackRoundTimeStamp = Number( - process.env.FIRST_GIVBACK_ROUND_TIME_STAMP, - ); - const givbackRoundLength = Number(process.env.GIVPOWER_ROUND_DURATION); - - const startBoundary = - firstGivbackRoundTimeStamp + (round - 1) * givbackRoundLength; - const endBoundary = startBoundary + givbackRoundLength; - const snapshotTime = (snapshot.time.getTime() as number) / 1000; - assert.isAtLeast(snapshotTime, startBoundary); - assert.isBelow(snapshotTime, endBoundary); - - snapshot.roundNumber = round; - await snapshot.save(); - [snapshot] = (await PowerSnapshot.find({ take: 1 })) as PowerSnapshot[]; - assert.equal(snapshot.roundNumber, round); - }); -} diff --git a/src/repositories/powerBoostingRepository.ts b/src/repositories/powerBoostingRepository.ts deleted file mode 100644 index 9b255d488..000000000 --- a/src/repositories/powerBoostingRepository.ts +++ /dev/null @@ -1,429 +0,0 @@ -import { Brackets } from 'typeorm'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { Project } from '../entities/project'; -import { publicSelectionFields, User } from '../entities/user'; -import { logger } from '../utils/logger'; -import { - errorMessages, - i18n, - translationErrorMessagesKeys, -} from '../utils/errorMessages'; -import { PowerSnapshot } from '../entities/powerSnapshot'; -import { getRoundNumberByDate } from '../utils/powerBoostingUtils'; -import { getKeyByValue } from '../utils/utils'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { AppDataSource } from '../orm'; - -const MAX_PROJECT_BOOST_LIMIT = Number( - process.env.GIVPOWER_BOOSTING_USER_PROJECTS_LIMIT || '20', -); -const PERCENTAGE_PRECISION = Number( - process.env.GIVPOWER_BOOSTING_PERCENTAGE_PRECISION || '2', -); - -const formatPercentage = (p: number): number => { - return +p.toFixed(PERCENTAGE_PRECISION); -}; - -export const findUserPowerBoostings = async ( - userId?: number, - projectId?: number, - take?: number, - skip?: number, - forceProjectIds?: number[], -): Promise<[PowerBoosting[], number]> => { - const query = PowerBoosting.createQueryBuilder('powerBoosting') - .leftJoinAndSelect('powerBoosting.project', 'project') - .leftJoinAndSelect('powerBoosting.user', 'user'); - - if (userId) { - query.where(`powerBoosting.userId = :userId`, { userId }); - } - - if (projectId) { - query.where('powerBoosting.projectId = :projectId', { projectId }); - } - - if (!forceProjectIds || forceProjectIds.length === 0) { - query.andWhere(`percentage > 0`); - } else { - query.andWhere( - new Brackets(qb => - qb - .where('percentage > 0') - .orWhere(`powerBoosting.projectId IN (:...forceProjectIds)`, { - forceProjectIds, - }), - ), - ); - } - return query.take(take).skip(skip).getManyAndCount(); -}; - -export const findUserProjectPowerBoostingsSnapshots = async ( - userId?: number, - projectId?: number, - take?: number, - skip?: number, - powerSnapshotId?: number, - round?: number, -) => { - const query = PowerBoostingSnapshot.createQueryBuilder('powerBoosting') - .leftJoinAndSelect('powerBoosting.powerSnapshot', 'powerSnapshot') - .where(`percentage > 0`); - - if (userId) { - query.andWhere(`powerBoosting.userId = :userId`, { userId }); - } - - if (projectId) { - query.andWhere('powerBoosting.projectId = :projectId', { projectId }); - } - - if (round) { - query.andWhere('powerSnapshot.roundNumber = :round', { round }); - } - - if (powerSnapshotId) { - query.andWhere('powerBoosting.powerSnapshotId = :powerSnapshotId', { - powerSnapshotId, - }); - } - - return query.take(take).skip(skip).getManyAndCount(); -}; - -export const findUserPowerBoosting = async ( - userId: number, - forceProjectIds?: number[], -): Promise => { - const query = PowerBoosting.createQueryBuilder('powerBoosting') - .leftJoinAndSelect('powerBoosting.project', 'project') - .leftJoinAndSelect('powerBoosting.user', 'user') - .where(`"userId" =${userId}`); - - if (!forceProjectIds || forceProjectIds.length === 0) { - return query.andWhere(`percentage > 0`).getMany(); - } else { - return query - .andWhere( - new Brackets(qb => - qb - .where('percentage > 0') - .orWhere(`powerBoosting.projectId IN (:...forceProjectIds)`, { - forceProjectIds, - }), - ), - ) - .getMany(); - } -}; - -export const findPowerBoostings = async (params: { - take?: number; - skip?: number; - orderBy: { - field: 'createdAt' | 'updatedAt' | 'percentage'; - direction: 'ASC' | 'DESC'; - }; - userId?: number; - projectId?: number; -}): Promise<[PowerBoosting[], number]> => { - const query = PowerBoosting.createQueryBuilder('powerBoosting') - // select some parameters of project and user not all fields - .leftJoinAndSelect('powerBoosting.project', 'project') - .leftJoin('powerBoosting.user', 'user') - .addSelect(publicSelectionFields) - .where(`percentage > 0`); - - if (params.userId) { - query.andWhere(`"userId" =${params.userId}`); - } - if (params.projectId) { - query.andWhere(`"projectId" =${params.projectId}`); - } - query.orderBy( - `powerBoosting.${params.orderBy.field}`, - params.orderBy.direction, - ); - - if (params.take) { - query.take(params.take); - } - if (params.skip) { - query.skip(params.skip); - } - return query.getManyAndCount(); -}; - -export const findPowerBoostingsCountByUserId = async ( - userId: number, -): Promise => { - const query = PowerBoosting.createQueryBuilder('powerBoosting') - // select some parameters of project and user not all fields - .leftJoinAndSelect('powerBoosting.project', 'project') - .leftJoin('powerBoosting.user', 'user') - .addSelect(publicSelectionFields) - .where(`percentage > 0`) - .andWhere(`"userId" =${userId}`) - .cache( - `findPowerBoostingsCountByUserId-recurring-${userId}`, - Number(process.env.USER_STATS_CACHE_TIME || 60000), - ); - return query.getCount(); -}; - -export const insertSinglePowerBoosting = async (params: { - user: User; - project: Project; - percentage: number; -}): Promise => { - return PowerBoosting.create({ - user: params.user, - project: params.project, - percentage: params.percentage, - }).save(); -}; - -export const cancelProjectBoosting = async (params: { - userId: number; - projectId: number; -}): Promise => - _setSingleBoosting({ - ...params, - percentage: 0, - projectIsCanceled: true, - }); - -export const setSingleBoosting = async (params: { - userId: number; - projectId: number; - percentage: number; -}): Promise => - _setSingleBoosting({ ...params, projectIsCanceled: false }); - -const _setSingleBoosting = async (params: { - userId: number; - projectId: number; - percentage: number; - projectIsCanceled: boolean; -}): Promise => { - const { userId, projectId, percentage, projectIsCanceled } = params; - - if (percentage < 0 || percentage > 100) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ), - ); - } - - const queryRunner = AppDataSource.getDataSource().createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); - - let result: PowerBoosting[] = []; - - try { - const userPowerBoostings = await findUserPowerBoosting(userId, [projectId]); - - const otherProjectsPowerBoostings = userPowerBoostings.filter( - pb => +pb.projectId !== projectId, - ); - - // The power boosting corresponding to pair - let projectBoost = userPowerBoostings.find( - pb => +pb.projectId === projectId, - ); - - const commitData: PowerBoosting[] = []; - - if (otherProjectsPowerBoostings.length === 0) { - if (percentage !== 100 && !projectIsCanceled) - throw new Error( - i18n.__( - translationErrorMessagesKeys.ERROR_GIVPOWER_BOOSTING_FIRST_PROJECT_100_PERCENT, - ), - ); - } else { - if ( - otherProjectsPowerBoostings.length + 1 > MAX_PROJECT_BOOST_LIMIT && - percentage !== 100 - ) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.ERROR_GIVPOWER_BOOSTING_MAX_PROJECT_LIMIT, - ), - ); - } - - const otherProjectsCurrentTotalPercentages = - otherProjectsPowerBoostings.reduce( - (_sum, pb) => _sum + pb.percentage, - 0, - ); - const otherProjectsAfterTotalPercentages = 100 - percentage; - - otherProjectsPowerBoostings.forEach(_pb => { - _pb.percentage = formatPercentage( - (_pb.percentage * otherProjectsAfterTotalPercentages) / - otherProjectsCurrentTotalPercentages, - ); - commitData.push(_pb); - }); - } - - if (projectBoost) { - projectBoost.percentage = percentage; - } else { - projectBoost = PowerBoosting.create({ - userId, - projectId, - percentage, - }); - } - - commitData.push(projectBoost); - - await queryRunner.manager.save(commitData); - - await queryRunner.commitTransaction(); - - result = await findUserPowerBoosting(userId); - } catch (e) { - logger.error('setSingleBoosting error', e); - - // since we have errors let's rollback changes we made - await queryRunner.rollbackTransaction(); - const errorKey = getKeyByValue(errorMessages, e.message); - if (errorKey) - throw new Error(i18n.__(translationErrorMessagesKeys[errorKey])); - else - throw Error(i18n.__(translationErrorMessagesKeys.SOMETHING_WENT_WRONG)); - } finally { - await queryRunner.release(); - } - return result; -}; - -export const setMultipleBoosting = async (params: { - userId: number; - projectIds: number[]; - percentages: number[]; -}): Promise => { - const { userId, projectIds, percentages } = params; - - if (percentages.length > MAX_PROJECT_BOOST_LIMIT) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.ERROR_GIVPOWER_BOOSTING_MAX_PROJECT_LIMIT, - ), - ); - } - - if ( - percentages.length === 0 || - percentages.length !== projectIds.length || - new Set(projectIds).size !== projectIds.length || - percentages.some(percentage => percentage < 0 || percentage > 100) - ) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ), - ); - } - - const total: number = percentages.reduce( - (_sum, _percentage) => _sum + _percentage, - 0, - ); - - // Sometimes js add some tiny numbers at the end of number for instance if you - // calculate 50.46+18+12.62+9.46+9.46 with calculator you would get 100 but with js you will get - // 100.00000000000003 so we have to ignore small different changes in this webservice - const MAX_TOTAL_PERCENTAGES = 100.00001; - if ( - total < 100 - 0.01 * percentages.length || - total > MAX_TOTAL_PERCENTAGES - ) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ), - ); - } - - const map = new Map( - projectIds.map((projectId, index) => [projectId, percentages[index]]), - ); - - const queryRunner = AppDataSource.getDataSource().createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); - - let result: PowerBoosting[] = []; - - try { - const userPowerBoostings = await findUserPowerBoosting(userId, projectIds); - userPowerBoostings.forEach(pb => { - const projectId = +pb.projectId; - if (map.has(projectId)) { - pb.percentage = map.get(projectId) as number; - map.delete(projectId); - } else { - pb.percentage = 0; - } - }); - for (const [projectId, percentage] of map.entries()) { - userPowerBoostings.push( - PowerBoosting.create({ userId, projectId, percentage }), - ); - } - await queryRunner.manager.save(userPowerBoostings); - - await queryRunner.commitTransaction(); - - result = await findUserPowerBoosting(userId); - } catch (e) { - logger.error('setSingleBoosting error', e); - - // since we have errors let's rollback changes we made - await queryRunner.rollbackTransaction(); - const errorKey = getKeyByValue(errorMessages, e.message); - if (errorKey) - throw new Error(i18n.__(translationErrorMessagesKeys[errorKey])); - else - throw Error(i18n.__(translationErrorMessagesKeys.SOMETHING_WENT_WRONG)); - } finally { - await queryRunner.release(); - } - return result; -}; - -export const takePowerBoostingSnapshot = async () => { - await AppDataSource.getDataSource().query( - 'CALL public."TAKE_POWER_BOOSTING_SNAPSHOT"()', - ); -}; - -export const getPowerBoostingSnapshotRound = ( - snapshot: PowerSnapshot, -): number => { - return getRoundNumberByDate(snapshot.time).round; -}; - -export const getBoosterUsersByWalletAddresses = async ( - addressesLowercase: string[], -): Promise[]> => { - if (addressesLowercase.length === 0) return []; - // Return users has boosted projects and their wallet addresses are in addresses array - return await User.createQueryBuilder('user') - .where('LOWER(user.walletAddress) IN (:...addresses)', { - addresses: addressesLowercase, - }) - .andWhereExists( - PowerBoosting.createQueryBuilder('pb').where('pb.userId = user.id'), - ) - .getMany(); -}; diff --git a/src/repositories/powerRoundRepository.test.ts b/src/repositories/powerRoundRepository.test.ts deleted file mode 100644 index 53136aaa1..000000000 --- a/src/repositories/powerRoundRepository.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { assert } from 'chai'; -import { assertThrowsAsync } from '../../test/testUtils'; -import { getPowerRound, setPowerRound } from './powerRoundRepository'; -import { PowerRound } from '../entities/powerRound'; - -describe('powerRoundRepository testCases', () => { - it('should return correct round after setting', async () => { - let round: PowerRound | null = await setPowerRound(12); - assert.equal(round.round, 12); - assert.equal(await PowerRound.count(), 1); - - round = await getPowerRound(); - assert.isDefined(round); - assert.equal(round?.round, 12); - - await setPowerRound(35); - assert.equal(await PowerRound.count(), 1); - round = await getPowerRound(); - assert.equal(round?.round, 35); - }); - - it('should reject having multiple power rounds', async () => { - let round: PowerRound | null = await setPowerRound(12); - assert.equal(round.round, 12); - round = await getPowerRound(); - assert.isDefined(round); - assert.equal(round?.round, 12); - - assert.equal(await PowerRound.count(), 1); - - await assertThrowsAsync( - () => - PowerRound.create({ - id: false, - round: 15, - }).save(), - '', - ); - - assert.equal(await PowerRound.count(), 1); - - await PowerRound.create({ - id: true, - round: 14, - }).save(); - - assert.equal(await PowerRound.count(), 1); - }); -}); diff --git a/src/repositories/powerRoundRepository.ts b/src/repositories/powerRoundRepository.ts deleted file mode 100644 index dda8d6d91..000000000 --- a/src/repositories/powerRoundRepository.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { PowerRound } from '../entities/powerRound'; - -export const setPowerRound = async (round: number): Promise => { - let powerRound = await PowerRound.findOne({ where: {} }); - - if (!powerRound) { - powerRound = PowerRound.create({ - id: true, - round, - }); - } else { - powerRound.round = round; - } - return powerRound.save(); -}; - -export const getPowerRound = (): Promise => - PowerRound.findOne({ where: {} }); diff --git a/src/repositories/powerSnapshotRepository.test.ts b/src/repositories/powerSnapshotRepository.test.ts deleted file mode 100644 index 51360e49b..000000000 --- a/src/repositories/powerSnapshotRepository.test.ts +++ /dev/null @@ -1,433 +0,0 @@ -import { assert } from 'chai'; -import { PowerSnapshot } from '../entities/powerSnapshot'; -import { - getPowerBoostingSnapshotWithoutBalance, - updatePowerSnapshotSyncedFlag, -} from './powerSnapshotRepository'; -import { - createProjectData, - generateRandomEtheriumAddress, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, -} from '../../test/testUtils'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { AppDataSource } from '../orm'; -import { addOrUpdatePowerSnapshotBalances } from './powerBalanceSnapshotRepository'; -import { getTimestampInSeconds } from '../utils/utils'; - -describe('findPowerSnapshotById() test cases', findPowerSnapshotByIdTestCases); -describe('test balance snapshot functions', balanceSnapshotTestCases); - -function balanceSnapshotTestCases() { - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - it('should return power snapshots with not corresponding balance snapshot', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - let powerSnapshotTime = user1.id * 1000; - - const powerSnapshots = PowerSnapshot.create([ - { - time: new Date(powerSnapshotTime++), - }, - { - time: new Date(powerSnapshotTime++), - }, - ]); - await PowerSnapshot.save(powerSnapshots); - - const powerBoostingSnapshots = PowerBoostingSnapshot.create([ - { - userId: user1.id, - projectId: project1.id, - percentage: 10, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user2.id, - projectId: project1.id, - percentage: 20, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user1.id, - projectId: project1.id, - percentage: 11, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user2.id, - projectId: project1.id, - percentage: 21, - powerSnapshot: powerSnapshots[1], - }, - ]); - await PowerBoostingSnapshot.save(powerBoostingSnapshots); - - const powerBalances = PowerBalanceSnapshot.create([ - { - userId: user1.id, - balance: 1, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user1.id, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[1], - }, - ]); - - await PowerBalanceSnapshot.save(powerBalances); - - const result = await getPowerBoostingSnapshotWithoutBalance(); - assert.lengthOf(result, 3); - }); - it('should return user wallet address alongside power snapshots', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - let powerSnapshotTime = user1.id * 1000; - - const powerSnapshots = PowerSnapshot.create([ - { - time: new Date(powerSnapshotTime++), - blockNumber: 100, - }, - { - time: new Date(powerSnapshotTime++), - }, - ]); - await PowerSnapshot.save(powerSnapshots); - - const powerBoostingSnapshots = PowerBoostingSnapshot.create([ - { - userId: user1.id, - projectId: project1.id, - percentage: 10, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user2.id, - projectId: project1.id, - percentage: 20, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user1.id, - projectId: project1.id, - percentage: 11, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user2.id, - projectId: project1.id, - percentage: 21, - powerSnapshot: powerSnapshots[1], - }, - ]); - await PowerBoostingSnapshot.save(powerBoostingSnapshots); - - const powerBalances = PowerBalanceSnapshot.create([ - { - userId: user1.id, - balance: 1, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user1.id, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[1], - }, - ]); - - await PowerBalanceSnapshot.save(powerBalances); - - PowerBalanceSnapshot.create({ - userId: user1.id, - powerSnapshot: powerSnapshots[0], - balance: 10, - }); - - const result = await getPowerBoostingSnapshotWithoutBalance(); - assert.lengthOf(result, 3); - }); - - it('should return power snapshots with not corresponding balance snapshot - pagination', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user3 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - let powerSnapshotTime = user1.id * 1_000_000; - - const powerSnapshots = PowerSnapshot.create([ - { - time: new Date((powerSnapshotTime = powerSnapshotTime + 1000)), - }, - { - time: new Date((powerSnapshotTime = powerSnapshotTime + 1000)), - }, - { - time: new Date((powerSnapshotTime = powerSnapshotTime + 1000)), - }, - ]); - await PowerSnapshot.save(powerSnapshots); - - const powerBoostingSnapshots = PowerBoostingSnapshot.create([ - { - userId: user1.id, - projectId: project1.id, - percentage: 10, - powerSnapshot: powerSnapshots[2], - }, - { - userId: user2.id, - projectId: project1.id, - percentage: 20, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user3.id, - projectId: project1.id, - percentage: 30, - powerSnapshot: powerSnapshots[0], - }, - ]); - await PowerBoostingSnapshot.save(powerBoostingSnapshots); - - const powerBalances = PowerBalanceSnapshot.create([ - { - userId: user1.id, - powerSnapshot: powerSnapshots[2], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user3.id, - powerSnapshot: powerSnapshots[0], - }, - ]); - - await PowerBalanceSnapshot.save(powerBalances); - - // Only one slot - // Must return corresponding to the first snapshot and only one - let result = await getPowerBoostingSnapshotWithoutBalance(1, 0); - assert.lengthOf(result, 1); - assert.deepEqual(result[0], { - userId: user3.id, - powerSnapshotId: powerSnapshots[0].id, - walletAddress: user3.walletAddress as string, - timestamp: getTimestampInSeconds(powerSnapshots[0].time), - }); - - // Must return 2 last items in order - result = await getPowerBoostingSnapshotWithoutBalance(2, 1); - assert.lengthOf(result, 2); - assert.deepEqual(result, [ - { - userId: user2.id, - powerSnapshotId: powerSnapshots[1].id, - walletAddress: user2.walletAddress as string, - timestamp: getTimestampInSeconds(powerSnapshots[1].time), - }, - { - userId: user1.id, - walletAddress: user1.walletAddress, - powerSnapshotId: powerSnapshots[2].id, - timestamp: getTimestampInSeconds(powerSnapshots[2].time), - }, - ]); - }); -} - -function findPowerSnapshotByIdTestCases() { - it('should set synced snapshots flag', async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - - let powerSnapshotTime = user1.id * 1000; - - const powerSnapshots = PowerSnapshot.create([ - { - time: new Date(powerSnapshotTime++), - }, - { - time: new Date(powerSnapshotTime++), - }, - { - time: new Date(powerSnapshotTime++), - }, - { - time: new Date(powerSnapshotTime++), - }, - ]); - - await PowerSnapshot.save(powerSnapshots); - - const [firstSnapshot, secondSnapshot, thirdSnapshot, forthSnapshot] = - powerSnapshots; - - const powerBoostingSnapshots = PowerBoostingSnapshot.create([ - { - userId: user1.id, - projectId: project1.id, - percentage: 10, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user1.id, - projectId: project1.id, - percentage: 20, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user1.id, - projectId: project1.id, - percentage: 30, - powerSnapshot: powerSnapshots[2], - }, - { - userId: user1.id, - projectId: project1.id, - percentage: 40, - powerSnapshot: powerSnapshots[3], - }, - ]); - - await PowerBoostingSnapshot.save(powerBoostingSnapshots); - - const powerBalances = PowerBalanceSnapshot.create([ - { - userId: user1.id, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user1.id, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user1.id, - powerSnapshot: powerSnapshots[2], - }, - { - userId: user1.id, - powerSnapshot: powerSnapshots[3], - }, - ]); - - await PowerBalanceSnapshot.save(powerBalances); - - // No snapshot has blockNumber - let updateFlatResponse = await updatePowerSnapshotSyncedFlag(); - - assert.equal(updateFlatResponse, 0); - - // firstSnapshot with blockNumber without balance saved. - - firstSnapshot.blockNumber = 1000; - await firstSnapshot.save(); - updateFlatResponse = await updatePowerSnapshotSyncedFlag(); - - assert.equal(updateFlatResponse, 0); - - // Filled the balance for the first snapshot - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - balance: 1, - powerSnapshotId: firstSnapshot.id, - }); - - updateFlatResponse = await updatePowerSnapshotSyncedFlag(); - assert.equal(updateFlatResponse, 1); - await firstSnapshot.reload(); - assert.isTrue(firstSnapshot.synced); - - // Fill only the third snapshot info - - thirdSnapshot.blockNumber = 3000; - await thirdSnapshot.save(); - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - balance: 1, - powerSnapshotId: thirdSnapshot.id, - }); - updateFlatResponse = await updatePowerSnapshotSyncedFlag(); - assert.equal(updateFlatResponse, 1); - await thirdSnapshot.reload(); - assert.isTrue(thirdSnapshot.synced); - - // Fill second and forth snapshots info - secondSnapshot.blockNumber = 2000; - forthSnapshot.blockNumber = 4000; - await PowerSnapshot.save([secondSnapshot, forthSnapshot]); - await addOrUpdatePowerSnapshotBalances([ - { - userId: user1.id, - balance: 1, - powerSnapshotId: secondSnapshot.id, - }, - { - userId: user1.id, - balance: 1, - powerSnapshotId: forthSnapshot.id, - }, - ]); - updateFlatResponse = await updatePowerSnapshotSyncedFlag(); - assert.equal(updateFlatResponse, 2); - await secondSnapshot.reload(); - await forthSnapshot.reload(); - assert.isTrue(secondSnapshot.synced); - assert.isTrue(forthSnapshot.synced); - }); -} diff --git a/src/repositories/powerSnapshotRepository.ts b/src/repositories/powerSnapshotRepository.ts deleted file mode 100644 index d673bb25c..000000000 --- a/src/repositories/powerSnapshotRepository.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { PowerSnapshot } from '../entities/powerSnapshot'; -import { AppDataSource } from '../orm'; - -export const findInCompletePowerSnapShots = async (): Promise< - PowerSnapshot[] -> => { - return PowerSnapshot.createQueryBuilder() - .where('"roundNumber" IS NULL') - .getMany(); -}; - -export const updatePowerSnapShots = async (params: { - roundNumber: number; - powerSnapshot: PowerSnapshot; -}): Promise => { - const { roundNumber, powerSnapshot } = params; - powerSnapshot.roundNumber = roundNumber; - await powerSnapshot.save(); -}; - -export const findPowerSnapshots = async ( - round?: number, - powerSnapshotId?: number, - take: number = 100, - skip: number = 0, -) => { - const query = PowerSnapshot.createQueryBuilder('powerSnapshot'); - - if (round && !powerSnapshotId) { - query.where('powerSnapshot.roundNumber = :round', { round }); - } else if (!round && powerSnapshotId) { - query.where('powerSnapshot.id = :id', { id: powerSnapshotId }); - } else if (round && powerSnapshotId) { - query - .where('powerSnapshot.id = :id', { id: powerSnapshotId }) - .andWhere('powerSnapshot.roundNumber = :round', { round }); - } - - return query.take(take).skip(skip).getManyAndCount(); -}; - -export interface GetPowerBoostingSnapshotWithoutBalanceOutput { - userId: number; - timestamp: number; - powerSnapshotId: number; - walletAddress: string; -} -export const getPowerBoostingSnapshotWithoutBalance = async ( - limit = 50, - offset = 0, -): Promise => { - return await AppDataSource.getDataSource().query( - ` - select "userId", "powerSnapshotId", "walletAddress", floor(extract (epoch from "time" AT TIME ZONE 'UTC')) as "timestamp" - from public."power_balance_snapshot" as balanceSnapshot - inner join public."user" as "user" on "userId"= "user".id - inner join power_snapshot as "snapshot" on balanceSnapshot."powerSnapshotId" = snapshot.id - where balanceSnapshot.balance is null - order by "powerSnapshotId", "userId" - LIMIT $1 - OFFSET $2 - `, - [limit, offset], - ); -}; - -export const updatePowerSnapshotSyncedFlag = async (): Promise => { - const result = await AppDataSource.getDataSource().query( - ` - update power_snapshot as "snapshot" set synced = true - where - "snapshot"."synced" is not true and - not exists ( - select id - from power_balance_snapshot - where "powerSnapshotId" = snapshot.id and "balance" is null - )`, - ); - return result[1]; -}; diff --git a/src/repositories/previousRoundRankRepository.test.ts b/src/repositories/previousRoundRankRepository.test.ts deleted file mode 100644 index cd418446a..000000000 --- a/src/repositories/previousRoundRankRepository.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { assert } from 'chai'; -import { - copyProjectRanksToPreviousRoundRankTable, - deleteAllPreviousRoundRanks, - projectsThatTheirRanksHaveChanged, -} from './previousRoundRankRepository'; -import { AppDataSource } from '../orm'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { - createProjectData, - generateRandomEtheriumAddress, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, - SEED_DATA, - dbIndependentTests, -} from '../../test/testUtils'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from './powerBoostingRepository'; -import { getPowerRound, setPowerRound } from './powerRoundRepository'; -import { - getProjectPowers, - refreshProjectPowerView, -} from './projectPowerViewRepository'; -import { Project } from '../entities/project'; -import { PreviousRoundRank } from '../entities/previousRoundRank'; -import { ProjectPowerView } from '../views/projectPowerView'; -import { findProjectById } from './projectRepository'; -import { PowerRound } from '../entities/powerRound'; -import { addOrUpdatePowerSnapshotBalances } from './powerBalanceSnapshotRepository'; -import { findPowerSnapshots } from './powerSnapshotRepository'; - -describe( - 'copyProjectRanksToPreviousRoundRankTable test cases', - copyProjectRanksToPreviousRoundRankTableTestCases, -); -describe( - 'deleteAllPreviousRoundRanks test cases', - deleteAllPreviousRoundRanksTestCases, -); -describe( - 'projectsThatTheirRanksHaveChanged test cases', - projectsThatTheirRanksHaveChangedTestCases, -); - -beforeEach(async function () { - const { title } = this.currentTest?.parent || {}; - - if (title && dbIndependentTests.includes(title)) { - return; - } - - await AppDataSource.getDataSource().query('TRUNCATE power_snapshot CASCADE'); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - await PreviousRoundRank.clear(); - await PowerRound.clear(); - - await createSomeSampleProjectsAndPowerViews(); -}); - -const createSomeSampleProjectsAndPowerViews = async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project1.id * 10; - - await insertSinglePowerBoosting({ - user, - project: project1, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user, - project: project2, - percentage: 20, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - - await setPowerRound(roundNumber); - await refreshProjectPowerView(); -}; - -function copyProjectRanksToPreviousRoundRankTableTestCases() { - it('should copy projects rank in previous round rank table', async () => { - const projectPowerViews = await getProjectPowers(); - await copyProjectRanksToPreviousRoundRankTable(); - const result = await PreviousRoundRank.find(); - for (const projectPowerView of projectPowerViews) { - assert.equal( - projectPowerView.powerRank, - result.find(item => item.projectId === projectPowerView.projectId) - ?.rank, - ); - } - }); -} - -function deleteAllPreviousRoundRanksTestCases() { - it('should delete all previous round ranks', async () => { - await copyProjectRanksToPreviousRoundRankTable(); - assert.notEqual(await PreviousRoundRank.count(), 0); - await deleteAllPreviousRoundRanks(); - assert.equal(await PreviousRoundRank.count(), 0); - }); -} - -function projectsThatTheirRanksHaveChangedTestCases() { - it('should return projects that their ranks have changed', async () => { - await copyProjectRanksToPreviousRoundRankTable(); - - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await findProjectById(SEED_DATA.FIRST_PROJECT.id); - await saveProjectDirectlyToDb(createProjectData()); - const roundNumber = ((await getPowerRound())?.round as number) + 1; - - await insertSinglePowerBoosting({ - user, - project: project1, - percentage: 50, - }); - - await insertSinglePowerBoosting({ - user, - project: project2 as Project, - percentage: 50, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = roundNumber; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - - // Put so much balance on that to reorder lots of ranks - balance: 1000000, - }); - - await setPowerRound(roundNumber); - await refreshProjectPowerView(); - const projectsHaveNewRankingInputParams = - await projectsThatTheirRanksHaveChanged(); - - for (const item of projectsHaveNewRankingInputParams) { - const projectPowerView = await ProjectPowerView.findOne({ - where: { projectId: item.projectId }, - }); - const projectPreviousRank = await PreviousRoundRank.findOne({ - where: { projectId: item.projectId }, - }); - const project = await findProjectById(item.projectId); - - assert.equal(project?.verified, true); - assert.notEqual(projectPowerView?.powerRank, projectPreviousRank?.rank); - assert.equal(item.newRank, projectPowerView?.powerRank); - assert.equal(item.oldRank, projectPreviousRank?.rank); - } - }); -} diff --git a/src/repositories/previousRoundRankRepository.ts b/src/repositories/previousRoundRankRepository.ts deleted file mode 100644 index 4ea964444..000000000 --- a/src/repositories/previousRoundRankRepository.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { PowerSnapshot } from '../entities/powerSnapshot'; -import { PreviousRoundRank } from '../entities/previousRoundRank'; - -export const deleteAllPreviousRoundRanks = async () => { - return PreviousRoundRank.query( - ` - DELETE FROM previous_round_rank - `, - ); -}; - -export const copyProjectRanksToPreviousRoundRankTable = async () => { - await deleteAllPreviousRoundRanks(); - return PreviousRoundRank.query( - ` - INSERT INTO previous_round_rank ("projectId", round, rank) - SELECT project_power_view."projectId", project_power_view.round, project_power_view."powerRank" - FROM project_power_view; - `, - ); -}; - -export const projectsThatTheirRanksHaveChanged = async (): Promise< - { - projectId: number; - newRank: number; - oldRank: number; - round: number; - }[] -> => { - const result = await PowerSnapshot.query( - ` - SELECT - project_power_view."projectId", - project_power_view.round, - project_power_view."powerRank" as "newRank", - previous_round_rank.rank as "oldRank" - FROM project_power_view - INNER JOIN previous_round_rank ON previous_round_rank."projectId" = project_power_view."projectId" - INNER JOIN project ON project_power_view."projectId" = project.id - WHERE project_power_view."powerRank" != previous_round_rank.rank - AND project.verified = true; - `, - ); - return result.map(item => { - return { - projectId: Number(item.projectId), - newRank: Number(item.newRank), - oldRank: Number(item.oldRank), - round: Number(item.round), - }; - }); -}; diff --git a/src/repositories/projectFuturePowerViewRepository.ts b/src/repositories/projectFuturePowerViewRepository.ts deleted file mode 100644 index 0779ab4bf..000000000 --- a/src/repositories/projectFuturePowerViewRepository.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ProjectFuturePowerView } from '../views/projectFuturePowerView'; - -export const findFuturePowers = async ( - projectIds: number[] = [], - round?: number, - take: number = 100, - skip: number = 0, -): Promise<[ProjectFuturePowerView[], number]> => { - const query = ProjectFuturePowerView.createQueryBuilder( - 'projectFuturePowerView', - ).leftJoinAndSelect('projectFuturePowerView.project', 'project'); - - if (projectIds.length > 0 && round) { - query - .where('projectFuturePowerView.projectId IN (:...projectIds)', { - projectIds, - }) - .andWhere('projectFuturePowerView.round = :round', { round }); - } else if (projectIds.length === 0 && round) { - query.where('projectFuturePowerView.round = :round', { round }); - } else if (projectIds.length > 0 && !round) { - query.where('projectFuturePowerView.projectId IN (:...projectIds)', { - projectIds, - }); - } - - return query.take(take).skip(skip).getManyAndCount(); -}; diff --git a/src/repositories/projectPowerViewRepository.test.ts b/src/repositories/projectPowerViewRepository.test.ts deleted file mode 100644 index 90e6890e7..000000000 --- a/src/repositories/projectPowerViewRepository.test.ts +++ /dev/null @@ -1,535 +0,0 @@ -import { assert, expect } from 'chai'; -import { - createProjectData, - generateRandomEtheriumAddress, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, - sleep, -} from '../../test/testUtils'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from './powerBoostingRepository'; -import { setPowerRound } from './powerRoundRepository'; -import { - getBottomRank, - getProjectPowers, - refreshProjectPowerView, - refreshProjectFuturePowerView, - getProjectFuturePowers, - findProjectPowerViewByProjectId, -} from './projectPowerViewRepository'; -import { Project, ProjStatus } from '../entities/project'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { ProjectStatus } from '../entities/projectStatus'; -import { AppDataSource } from '../orm'; -import { addOrUpdatePowerSnapshotBalances } from './powerBalanceSnapshotRepository'; -import { findPowerSnapshots } from './powerSnapshotRepository'; - -describe( - 'projectPowerViewRepository test', - projectPowerViewRepositoryTestCases, -); - -describe( - 'findProjectPowerViewByProjectId test', - findProjectPowerViewByProjectIdTestCases, -); - -describe( - 'projectFuturePowerViewRepository test', - projectFuturePowerViewRepositoryTestCases, -); - -describe('getBottomPowerRank test cases', getBottomPowerRankTestCases); - -function projectPowerViewRepositoryTestCases() { - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - it('should rank correctly, and include boosted and not boosted projects', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project1.id * 10; - - await insertSinglePowerBoosting({ - user, - project: project1, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user, - project: project2, - percentage: 20, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - - await setPowerRound(roundNumber); - await refreshProjectPowerView(); - const projectPowers = await getProjectPowers(project3.id); - const projectCount = await Project.count(); - assert.isArray(projectPowers); - assert.lengthOf(projectPowers, projectCount); - // the concurrently are affected by other tests TODO: FIX - // assert.equal(projectPowers[0].powerRank, 1); - // assert.equal(projectPowers[0].projectId, project2.id); - // assert.equal(projectPowers[1].powerRank, 2); - // assert.equal(projectPowers[1].projectId, project1.id); - // assert.equal(projectPowers[2].powerRank, 3); - // assert.equal(projectPowers[3].powerRank, 3); - }); - it('should rank correctly, exclude non-active projects', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - await saveProjectDirectlyToDb(createProjectData()); - const nonActiveProject = await saveProjectDirectlyToDb(createProjectData()); - const status = await ProjectStatus.findOne({ - where: { - id: ProjStatus.deactive, - }, - }); - nonActiveProject.status = status as ProjectStatus; - await nonActiveProject.save(); - - const roundNumber = project1.id * 10; - - await insertSinglePowerBoosting({ - user, - project: project1, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user, - project: project2, - percentage: 20, - }); - await insertSinglePowerBoosting({ - user, - project: nonActiveProject, - percentage: 20, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - - await setPowerRound(roundNumber); - await refreshProjectPowerView(); - const projectPowers = await getProjectPowers(nonActiveProject.id); - const projectCount = await Project.count(); - assert.isArray(projectPowers); - assert.lengthOf(projectPowers, projectCount); - assert.notEqual( - projectPowers.find(pb => pb.projectId === project1.id)?.totalPower, - 0, - ); - assert.notEqual( - projectPowers.find(pb => pb.projectId === project2.id)?.totalPower, - 0, - ); - assert.equal( - projectPowers.find(pb => pb.projectId === nonActiveProject.id) - ?.totalPower, - 0, - ); - }); - - it('should set correct power amount', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const roundNumber = project1.id * 10; - - const user1Boosting = await insertSinglePowerBoosting({ - user: user1, - project: project1, - percentage: 10, - }); - const user2Boosting = await insertSinglePowerBoosting({ - user: user2, - project: project1, - percentage: 20, - }); - - await takePowerBoostingSnapshot(); - let [powerSnapshots] = await findPowerSnapshots(); - let snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }, - { - userId: user2.id, - powerSnapshotId: snapshot.id, - balance: 20000, - }, - ]); - - await sleep(1); - - user1Boosting.percentage = 20; - await user1Boosting.save(); - - user2Boosting.percentage = 40; - await user2Boosting.save(); - - await takePowerBoostingSnapshot(); - [powerSnapshots] = await findPowerSnapshots(); - snapshot = powerSnapshots[1]; - - snapshot.blockNumber = 2; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 20000, - }, - { - userId: user2.id, - powerSnapshotId: snapshot.id, - balance: 40000, - }, - ]); - - await setPowerRound(roundNumber); - - await refreshProjectPowerView(); - const projectPowers = await getProjectPowers(project1.id + 1); - assert.isArray(projectPowers); - - const project1power = projectPowers.find(p => p.projectId === project1.id); - - assert.isDefined(project1power); - - // User1 power boosting = (10000 * 0.10 + 20000 * 0.20) / 2 = 2500 - // User2 power boosting = (20000 * 0.20 + 40000 * 0.40) / 2 = 10000 - expect(project1power?.totalPower).to.be.closeTo(10000 + 2500, 0.00001); - }); -} - -function findProjectPowerViewByProjectIdTestCases() { - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - it('Return project rank correctly', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project1.id * 10; - - await insertSinglePowerBoosting({ - user, - project: project1, - percentage: 10, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - - await setPowerRound(roundNumber); - await refreshProjectPowerView(); - const projectPower = await findProjectPowerViewByProjectId(project1.id); - assert.isOk(projectPower); - assert.equal(projectPower?.powerRank, 1); - assert.equal(projectPower?.totalPower, 10); - }); -} - -function projectFuturePowerViewRepositoryTestCases() { - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBoosting.clear(); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - it('should calculate future power rank correctly', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - const project4 = await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project1.id * 10; - - const boosting1 = await insertSinglePowerBoosting({ - user, - project: project1, - percentage: 10, - }); - const boosting2 = await insertSinglePowerBoosting({ - user, - project: project2, - percentage: 20, - }); - const boosting3 = await insertSinglePowerBoosting({ - user, - project: project3, - percentage: 30, - }); - const boosting4 = await insertSinglePowerBoosting({ - user, - project: project4, - percentage: 40, - }); - - await takePowerBoostingSnapshot(); - let [powerSnapshots] = await findPowerSnapshots(); - let snapshot = powerSnapshots[0]; - - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - - await setPowerRound(roundNumber); - - boosting1.percentage = 70; - boosting2.percentage = 30; - boosting3.percentage = 0; - boosting4.percentage = 0; - - await PowerBoosting.save([boosting1, boosting2, boosting3, boosting4]); - - await takePowerBoostingSnapshot(); - [powerSnapshots] = await findPowerSnapshots(); - snapshot = powerSnapshots[1]; - snapshot.roundNumber = roundNumber + 1; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - - await refreshProjectPowerView(); - await refreshProjectFuturePowerView(); - - const projectPowers = await getProjectPowers(project4.id); - const projectFuturePowers = await getProjectFuturePowers(project4.id); - - const projectCount = await Project.count(); - assert.isArray(projectPowers); - assert.lengthOf(projectPowers, projectCount); - // OTher tests affect this: TODO FIX - // assert.equal(projectPowers[0].powerRank, 1); - // assert.equal(projectPowers[0].projectId, project4.id); - // assert.equal(projectPowers[1].powerRank, 2); - // assert.equal(projectPowers[1].projectId, project3.id); - // assert.equal(projectPowers[2].powerRank, 3); - // assert.equal(projectPowers[2].projectId, project2.id); - // assert.equal(projectPowers[3].powerRank, 4); - // assert.equal(projectPowers[3].projectId, project1.id); - assert.isArray(projectFuturePowers); - // assert.equal(projectFuturePowers[0].powerRank, 1); - // assert.equal(projectFuturePowers[0].projectId, project1.id); - // assert.equal(projectFuturePowers[1].powerRank, 2); - // assert.equal(projectFuturePowers[1].projectId, project2.id); - }); - it('should calculate future power rank correctly, exclude nonActive projects', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const nonActiveProject = await saveProjectDirectlyToDb(createProjectData()); - const status = await ProjectStatus.findOne({ - where: { - id: ProjStatus.deactive, - }, - }); - nonActiveProject.status = status as ProjectStatus; - await nonActiveProject.save(); - - const roundNumber = project1.id * 10; - - const boosting1 = await insertSinglePowerBoosting({ - user, - project: project1, - percentage: 10, - }); - const boosting2 = await insertSinglePowerBoosting({ - user, - project: project2, - percentage: 20, - }); - const boosting3 = await insertSinglePowerBoosting({ - user, - project: nonActiveProject, - percentage: 30, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - - await setPowerRound(roundNumber); - - boosting1.percentage = 60; - boosting2.percentage = 30; - boosting3.percentage = 10; - - await PowerBoosting.save([boosting1, boosting2, boosting3]); - - await takePowerBoostingSnapshot(); - - snapshot.blockNumber = 2; - snapshot.roundNumber = roundNumber + 1; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - - await refreshProjectPowerView(); - await refreshProjectFuturePowerView(); - - const projectFuturePowers = await getProjectFuturePowers(); - assert.isArray(projectFuturePowers); - assert.notEqual( - projectFuturePowers.find(pb => pb.projectId === project1.id)?.totalPower, - 0, - ); - assert.notEqual( - projectFuturePowers.find(pb => pb.projectId === project2.id)?.totalPower, - 0, - ); - assert.equal( - projectFuturePowers.find(pb => pb.projectId === nonActiveProject.id) - ?.totalPower, - 0, - ); - }); - // - // it('should return null for future power when no snapshot is synced', async () => {}); -} - -function getBottomPowerRankTestCases() { - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - it('should return bottomPowerRank correctly', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - await saveProjectDirectlyToDb(createProjectData()); - await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project1.id * 10; - - await insertSinglePowerBoosting({ - user, - project: project1, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user, - project: project2, - percentage: 20, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - - await setPowerRound(roundNumber); - await refreshProjectPowerView(); - - const bottomPowerRank = await getBottomRank(); - assert.equal(bottomPowerRank, 3); - }); -} diff --git a/src/repositories/projectPowerViewRepository.ts b/src/repositories/projectPowerViewRepository.ts deleted file mode 100644 index 31318349a..000000000 --- a/src/repositories/projectPowerViewRepository.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { Not, MoreThan } from 'typeorm'; -import { FindOneOptions } from 'typeorm/find-options/FindOneOptions'; -import { ProjectPowerView } from '../views/projectPowerView'; -import { ProjectFuturePowerView } from '../views/projectFuturePowerView'; -import { logger } from '../utils/logger'; -import { updatePowerSnapshotSyncedFlag } from './powerSnapshotRepository'; -import { LastSnapshotProjectPowerView } from '../views/lastSnapshotProjectPowerView'; -import { AppDataSource } from '../orm'; - -export const getProjectPowers = async ( - take: number = 50, - skip: number = 0, -): Promise => { - return ProjectPowerView.find({ take, skip }); -}; - -export const findProjectsPowers = async ( - projectIds: number[] = [], - round?: number, - take: number = 100, - skip: number = 0, -): Promise<[ProjectPowerView[], number]> => { - const query = ProjectPowerView.createQueryBuilder( - 'projectPowerView', - ).leftJoinAndSelect('projectPowerView.project', 'project'); - - if (projectIds.length > 0 && round) { - query - .where('projectPowerView.projectId IN (:...projectIds)', { projectIds }) - .andWhere('projectPowerView.round = :round', { round }); - } else if (projectIds.length === 0 && round) { - query.where('projectPowerView.round = :round', { round }); - } else if (projectIds.length > 0 && !round) { - query.where('projectPowerView.projectId IN (:...projectIds)', { - projectIds, - }); - } - - return query.take(take).skip(skip).getManyAndCount(); -}; - -export const findProjectPowerViewByProjectId = async ( - projectId: number, -): Promise => { - return ProjectPowerView.findOne({ where: { projectId } }); -}; - -export const getProjectFuturePowers = async ( - take: number = 50, - skip: number = 0, -): Promise => { - return ProjectFuturePowerView.find({ take, skip }); -}; - -export const getBottomRank = async (): Promise => { - try { - const powerRank = await AppDataSource.getDataSource().query(` - SELECT MAX("powerRank") FROM project_power_view - `); - return Number(powerRank[0].max); - } catch (e) { - logger.error('getTopPowerRank error', e); - throw new Error('Error in getting last power rank'); - } -}; - -export const refreshProjectPowerView = async (): Promise => { - logger.debug('Refresh project_power_view materialized view'); - try { - return AppDataSource.getDataSource().query( - ` - REFRESH MATERIALIZED VIEW CONCURRENTLY project_power_view - `, - ); - } catch (e) { - logger.error('refreshProjectPowerView() error', e); - } -}; - -export const refreshProjectFuturePowerView = async ( - updateSyncedFlag: boolean = true, -): Promise => { - try { - if (updateSyncedFlag) { - const numberNewSyncedSnapshots = await updatePowerSnapshotSyncedFlag(); - if (numberNewSyncedSnapshots > 0) { - logger.debug( - 'Refresh last_snapshot_project_power_view materialized view', - ); - await AppDataSource.getDataSource().query( - ` - REFRESH MATERIALIZED VIEW CONCURRENTLY last_snapshot_project_power_view - `, - ); - } - } - - logger.debug('Refresh project_future_power_view materialized view'); - return AppDataSource.getDataSource().query( - ` - REFRESH MATERIALIZED VIEW CONCURRENTLY project_future_power_view - `, - ); - } catch (e) { - logger.error('refreshProjectFuturePowerView()', e); - } -}; - -// Return position of a project with powerAmount in the power ranking list -export const getPowerAmountRank = async ( - powerAmount: number, - projectId?: number, -): Promise => { - if (powerAmount < 0) throw new Error('Power Amount cannot be zero'); - - const where: FindOneOptions['where'] = { - totalPower: MoreThan(powerAmount), - }; - - if (projectId !== undefined) { - where.projectId = Not(projectId); - } - - const [aboveProject] = await LastSnapshotProjectPowerView.find({ - where, - select: ['powerRank'], - order: { - totalPower: 'ASC', - }, - take: 1, - }); - - return aboveProject ? +aboveProject.powerRank + 1 : 1; // There is not any other project -}; diff --git a/src/repositories/projectRepository.test.ts b/src/repositories/projectRepository.test.ts index cec4c5a14..c52914126 100644 --- a/src/repositories/projectRepository.test.ts +++ b/src/repositories/projectRepository.test.ts @@ -21,22 +21,10 @@ import { import { createProjectVerificationForm } from './projectVerificationRepository'; import { PROJECT_VERIFICATION_STATUSES } from '../entities/projectVerificationForm'; import { NETWORK_IDS } from '../provider'; -import { setPowerRound } from './powerRoundRepository'; -import { refreshProjectPowerView } from './projectPowerViewRepository'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from './powerBoostingRepository'; import { Project } from '../entities/project'; -import { User } from '../entities/user'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { AppDataSource } from '../orm'; import { SUMMARY_LENGTH } from '../constants/summary'; import { getHtmlTextSummary } from '../utils/utils'; import { generateRandomString } from '../utils/utils'; -import { addOrUpdatePowerSnapshotBalances } from './powerBalanceSnapshotRepository'; -import { findPowerSnapshots } from './powerSnapshotRepository'; describe( 'findProjectByWalletAddress test cases', @@ -51,7 +39,6 @@ describe( 'updateProjectWithVerificationForm test cases', updateProjectWithVerificationFormTestCases, ); -describe('order by totalPower', orderByTotalPower); describe( 'update descriptionSummary test cases', updateDescriptionSummaryTestCases, @@ -379,80 +366,6 @@ function verifyMultipleProjectsTestCases() { }); } -function orderByTotalPower() { - it('order by totalPower DESC', async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - - await Promise.all( - [ - [user1, project1, 10], - [user1, project2, 20], - [user1, project3, 30], - [user2, project1, 20], - [user2, project2, 40], - [user2, project3, 60], - ].map(item => { - const [user, project, percentage] = item as [User, Project, number]; - return insertSinglePowerBoosting({ - user, - project, - percentage, - }); - }), - ); - - const roundNumber = project3.id * 10; - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }, - { - userId: user2.id, - powerSnapshotId: snapshot.id, - balance: 20000, - }, - ]); - - await setPowerRound(roundNumber); - await refreshProjectPowerView(); - const query = Project.createQueryBuilder('project') - .leftJoinAndSelect('project.projectPower', 'projectPower') - .select('project.id') - .addSelect('projectPower.totalPower') - .take(project3.id) - .orderBy('projectPower.totalPower', 'DESC', 'NULLS LAST') - .take(project3.id + 1); - - const [projects] = await query.getManyAndCount(); - assert.isArray(projects); - assert.equal(projects[0]?.id, project3.id); - assert.equal(projects[1]?.id, project2.id); - assert.equal(projects[2]?.id, project1.id); - }); -} - function updateDescriptionSummaryTestCases() { const SHORT_DESCRIPTION = '
Short Description
'; const SHORT_DESCRIPTION_SUMMARY = 'Short Description'; diff --git a/src/repositories/projectRepository.ts b/src/repositories/projectRepository.ts index a00fedd7c..d60e9d3e4 100644 --- a/src/repositories/projectRepository.ts +++ b/src/repositories/projectRepository.ts @@ -108,12 +108,6 @@ export const filterProjectsQuery = (params: FilterProjectQueryInputParams) => { { isActive: true }, ) .leftJoinAndSelect('categories.mainCategory', 'mainCategory') - .leftJoin('project.projectPower', 'projectPower') - .addSelect([ - 'projectPower.totalPower', - 'projectPower.powerRank', - 'projectPower.round', - ]) .where( `project.statusId = ${ProjStatus.active} AND project.reviewStatus = :reviewStatus`, { reviewStatus: ReviewStatus.Listed }, @@ -137,14 +131,6 @@ export const filterProjectsQuery = (params: FilterProjectQueryInputParams) => { { qfRoundSlug }, ); } - if (!sortingBy || sortingBy === SortingField.InstantBoosting) { - query = query - .leftJoin('project.projectInstantPower', 'projectInstantPower') - .addSelect([ - 'projectInstantPower.totalPower', - 'projectInstantPower.powerRank', - ]); - } // Filters query = ProjectResolver.addCategoryQuery(query, category); @@ -176,7 +162,7 @@ export const filterProjectsQuery = (params: FilterProjectQueryInputParams) => { case SortingField.MostLiked: query.orderBy('project.totalReactions', OrderDirection.DESC); break; - case SortingField.Newest: + case SortingField.Newest: // This is our default sorting query.orderBy('project.creationDate', OrderDirection.DESC); break; case SortingField.RecentlyUpdated: @@ -188,34 +174,6 @@ export const filterProjectsQuery = (params: FilterProjectQueryInputParams) => { case SortingField.QualityScore: query.orderBy('project.qualityScore', OrderDirection.DESC); break; - case SortingField.GIVPower: - query - .orderBy(`project.verified`, OrderDirection.DESC) - .addOrderBy( - 'projectPower.totalPower', - OrderDirection.DESC, - 'NULLS LAST', - ); - break; - case SortingField.InstantBoosting: // This is our default sorting - query - .orderBy(`project.verified`, OrderDirection.DESC) - .addOrderBy( - 'projectInstantPower.totalPower', - OrderDirection.DESC, - 'NULLS LAST', - ); - if (isFilterByQF) { - query.addOrderBy( - 'project.sumDonationValueUsdForActiveQfRound', - OrderDirection.DESC, - 'NULLS LAST', - ); - } else { - query.addOrderBy('project.totalDonations', OrderDirection.DESC); - } - query.addOrderBy('project.totalReactions', OrderDirection.DESC); - break; case SortingField.ActiveQfRoundRaisedFunds: if (activeQfRoundId) { query @@ -240,9 +198,7 @@ export const filterProjectsQuery = (params: FilterProjectQueryInputParams) => { } break; default: - query - .orderBy('projectInstantPower.totalPower', OrderDirection.DESC) - .addOrderBy(`project.verified`, OrderDirection.DESC); + query.orderBy('project.creationDate', OrderDirection.DESC); break; } diff --git a/src/repositories/userProjectPowerViewRepository.test.ts b/src/repositories/userProjectPowerViewRepository.test.ts deleted file mode 100644 index 667daf43f..000000000 --- a/src/repositories/userProjectPowerViewRepository.test.ts +++ /dev/null @@ -1,353 +0,0 @@ -import { assert } from 'chai'; -import { - createProjectData, - generateRandomEtheriumAddress, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, - sleep, -} from '../../test/testUtils'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from './powerBoostingRepository'; -import { setPowerRound } from './powerRoundRepository'; -import { - getUserProjectPowers, - refreshUserProjectPowerView, -} from './userProjectPowerViewRepository'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { - UserPowerOrderDirection, - UserPowerOrderField, -} from '../resolvers/userProjectPowerResolver'; -import { AppDataSource } from '../orm'; -import { addOrUpdatePowerSnapshotBalances } from './powerBalanceSnapshotRepository'; -import { findPowerSnapshots } from './powerSnapshotRepository'; - -describe('userProjectPowerViewRepository test', () => { - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - it('should set correct power amount for different users', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project = await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project.id * 10; - - const user1Boosting = await insertSinglePowerBoosting({ - user: user1, - project, - percentage: 10, - }); - const user2Boosting = await insertSinglePowerBoosting({ - user: user2, - project, - percentage: 15, - }); - - await takePowerBoostingSnapshot(); - let [powerSnapshots] = await findPowerSnapshots(); - let snapshot = powerSnapshots[0]; - - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { userId: user1.id, powerSnapshotId: snapshot.id, balance: 100 }, - { userId: user2.id, powerSnapshotId: snapshot.id, balance: 200 }, - ]); - await sleep(1); - - user1Boosting.percentage = 20; - user2Boosting.percentage = 30; - await PowerBoosting.save([user1Boosting, user2Boosting]); - - await takePowerBoostingSnapshot(); - [powerSnapshots] = await findPowerSnapshots(); - snapshot = powerSnapshots[1]; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { userId: user1.id, powerSnapshotId: snapshot.id, balance: 200 }, - { userId: user2.id, powerSnapshotId: snapshot.id, balance: 400 }, - ]); - - await setPowerRound(roundNumber); - - await refreshUserProjectPowerView(); - - const [userPowers, count] = await getUserProjectPowers({ - take: 2, - skip: 0, - projectId: project.id, - orderBy: { - field: UserPowerOrderField.BoostedPower, - direction: UserPowerOrderDirection.DESC, - }, - }); - - assert.equal(count, 2); - assert.equal(userPowers[0].rank, 1); - assert.equal(userPowers[0].userId, user2.id); - assert.equal(userPowers[0].projectId, project.id); - assert.equal(userPowers[0].boostedPower, 75); // (0.15 * 200 + 0.30 * 400) / 2 = 75 - - assert.equal(userPowers[1].rank, 2); - assert.equal(userPowers[1].userId, user1.id); - assert.equal(userPowers[1].projectId, project.id); - assert.equal(userPowers[1].boostedPower, 25); // (0.10 * 100 + 0.20 * 200) / 2 = 25 - }); - - it('should have correct power amount for different rounds', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project = await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project.id * 10; - - const user1Boosting = await insertSinglePowerBoosting({ - user: user1, - project, - percentage: 10, - }); - const user2Boosting = await insertSinglePowerBoosting({ - user: user2, - project, - percentage: 15, - }); - - await takePowerBoostingSnapshot(); - let [powerSnapshots] = await findPowerSnapshots(); - let snapshot = powerSnapshots[0]; - - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { userId: user1.id, powerSnapshotId: snapshot.id, balance: 100 }, - { userId: user2.id, powerSnapshotId: snapshot.id, balance: 200 }, - ]); - await sleep(1); - - user1Boosting.percentage = 20; - user2Boosting.percentage = 30; - await PowerBoosting.save([user1Boosting, user2Boosting]); - - await takePowerBoostingSnapshot(); - [powerSnapshots] = await findPowerSnapshots(); - snapshot = powerSnapshots[1]; - - snapshot.blockNumber = 2; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { userId: user1.id, powerSnapshotId: snapshot.id, balance: 200 }, - { userId: user2.id, powerSnapshotId: snapshot.id, balance: 400 }, - ]); - - await sleep(1); - user1Boosting.percentage = 30; - user2Boosting.percentage = 45; - await PowerBoosting.save([user1Boosting, user2Boosting]); - - await takePowerBoostingSnapshot(); - [powerSnapshots] = await findPowerSnapshots(); - snapshot = powerSnapshots[2]; - snapshot.roundNumber = roundNumber + 1; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { userId: user1.id, powerSnapshotId: snapshot.id, balance: 300 }, - { userId: user2.id, powerSnapshotId: snapshot.id, balance: 50 }, - ]); - - await sleep(1); - user1Boosting.percentage = 40; - user2Boosting.percentage = 60; - await PowerBoosting.save([user1Boosting, user2Boosting]); - - await takePowerBoostingSnapshot(); - [powerSnapshots] = await findPowerSnapshots(); - snapshot = powerSnapshots[3]; - snapshot.roundNumber = roundNumber + 1; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { userId: user1.id, powerSnapshotId: snapshot.id, balance: 400 }, - { userId: user2.id, powerSnapshotId: snapshot.id, balance: 70 }, - ]); - - await setPowerRound(roundNumber); - - await refreshUserProjectPowerView(); - - let [userPowers, count] = await getUserProjectPowers({ - take: 2, - skip: 0, - projectId: project.id, - orderBy: { - field: UserPowerOrderField.BoostedPower, - direction: UserPowerOrderDirection.DESC, - }, - }); - - assert.equal(count, 2); - assert.equal(userPowers[0].rank, 1); - assert.equal(userPowers[0].userId, user2.id); - assert.equal(userPowers[0].boostedPower, 75); // (0.15 * 200 + 0.30 * 400) / 2 = 75 - - assert.equal(userPowers[1].rank, 2); - assert.equal(userPowers[1].userId, user1.id); - assert.equal(userPowers[1].boostedPower, 25); // (0.10 * 100 + 0.20 * 200) / 2 = 25 - - await setPowerRound(roundNumber + 1); - - await refreshUserProjectPowerView(); - - [userPowers, count] = await getUserProjectPowers({ - take: 2, - skip: 0, - projectId: project.id, - orderBy: { - field: UserPowerOrderField.BoostedPower, - direction: UserPowerOrderDirection.DESC, - }, - }); - - assert.equal(count, 2); - assert.equal(userPowers[0].rank, 1); - assert.equal(userPowers[0].userId, user1.id); - assert.equal(userPowers[0].boostedPower, 125); // (0.30 * 300 + 0.40 * 400) / 2 = 125 - - assert.equal(userPowers[1].rank, 2); - assert.equal(userPowers[1].userId, user2.id); - assert.equal(userPowers[1].boostedPower, 32.25); // (0.45 * 50 + 0.60 * 70) / 2 = 32.25 - }); - - it('should set rank correctly', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user3 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project1 = await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project1.id * 10; - - await insertSinglePowerBoosting({ - user: user1, - project: project1, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: user2, - project: project1, - percentage: 20, - }); - await insertSinglePowerBoosting({ - user: user3, - project: project1, - percentage: 30, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { userId: user1.id, powerSnapshotId: snapshot.id, balance: 100 }, - { userId: user2.id, powerSnapshotId: snapshot.id, balance: 200 }, - { userId: user3.id, powerSnapshotId: snapshot.id, balance: 300 }, - ]); - - await sleep(1); - await takePowerBoostingSnapshot(); - - snapshot.blockNumber = 2; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { userId: user1.id, powerSnapshotId: snapshot.id, balance: 200 }, - { userId: user2.id, powerSnapshotId: snapshot.id, balance: 300 }, - { userId: user3.id, powerSnapshotId: snapshot.id, balance: 400 }, - ]); - await sleep(1); - - await takePowerBoostingSnapshot(); - - snapshot.blockNumber = 3; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { userId: user1.id, powerSnapshotId: snapshot.id, balance: 300 }, - { userId: user2.id, powerSnapshotId: snapshot.id, balance: 400 }, - { userId: user3.id, powerSnapshotId: snapshot.id, balance: 500 }, - ]); - - await setPowerRound(roundNumber); - - await refreshUserProjectPowerView(); - - let [userPowers, count] = await getUserProjectPowers({ - take: 3, - skip: 0, - projectId: project1.id, - orderBy: { - field: UserPowerOrderField.BoostedPower, - direction: UserPowerOrderDirection.DESC, - }, - }); - - assert.equal(count, 3); - assert.isArray(userPowers); - assert.lengthOf(userPowers, 3); - - assert.deepEqual( - userPowers.map(p => p.userId), - [user3.id, user2.id, user1.id], - ); - - userPowers.forEach((p, i) => { - assert.equal(p.rank, i + 1); - }); - - // Pagination - [userPowers, count] = await getUserProjectPowers({ - take: 2, - skip: 1, - projectId: project1.id, - orderBy: { - field: UserPowerOrderField.BoostedPower, - direction: UserPowerOrderDirection.DESC, - }, - }); - assert.equal(count, 3); - assert.isArray(userPowers); - assert.lengthOf(userPowers, 2); - - assert.deepEqual( - userPowers.map(p => p.userId), - [user2.id, user1.id], - ); - - userPowers.forEach((p, i) => { - assert.equal(p.rank, i + 2); - }); - }); -}); diff --git a/src/repositories/userProjectPowerViewRepository.ts b/src/repositories/userProjectPowerViewRepository.ts deleted file mode 100644 index 9d1498bfa..000000000 --- a/src/repositories/userProjectPowerViewRepository.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { UserProjectPowerView } from '../views/userProjectPowerView'; -import { publicSelectionFields } from '../entities/user'; -import { logger } from '../utils/logger'; -import { UserPowerOrderBy } from '../resolvers/userProjectPowerResolver'; -import { AppDataSource } from '../orm'; - -export const getUserProjectPowers = async (params: { - take: number; - skip: number; - orderBy: UserPowerOrderBy; - userId?: number; - projectId?: number; - round?: number; -}): Promise<[UserProjectPowerView[], number]> => { - try { - const query = UserProjectPowerView.createQueryBuilder('userProjectPower') - .leftJoin('userProjectPower.user', 'user') - .addSelect(publicSelectionFields) - .addSelect( - 'RANK () OVER (ORDER BY "boostedPower" DESC)', - 'userProjectPower_rank', - ) - .where(`"boostedPower" > 0`); - - if (params.userId) { - query.andWhere(`"userId" =:userId`, { - userId: params.userId, - }); - } - if (params.projectId) { - query.andWhere(`"projectId" =:projectId`, { - projectId: params.projectId, - }); - } - if (params.round) { - query.andWhere(`"round" = :round`, { - round: params.round, - }); - } - return await query - .orderBy( - `userProjectPower.${params.orderBy.field}`, - params.orderBy.direction, - ) - .limit(params.take) - .offset(params.skip) - .getManyAndCount(); - // return [await query.getMany(), await query.getCount()] - } catch (e) { - logger.error('getUserProjectPowers error', e); - throw e; - } -}; - -export const refreshUserProjectPowerView = async (): Promise => { - logger.debug('Refresh user_project_power_view materialized view'); - return AppDataSource.getDataSource().query( - ` - REFRESH MATERIALIZED VIEW CONCURRENTLY user_project_power_view - `, - ); -}; diff --git a/src/repositories/userRepository.test.ts b/src/repositories/userRepository.test.ts index 585e4b4a5..84336a6d5 100644 --- a/src/repositories/userRepository.test.ts +++ b/src/repositories/userRepository.test.ts @@ -13,13 +13,11 @@ import { findAllUsers, findUserById, findUserByWalletAddress, - findUsersWhoBoostedProject, findUsersWhoDonatedToProjectExcludeWhoLiked, findUsersWhoLikedProjectExcludeProjectOwner, findUsersWhoSupportProject, } from './userRepository'; import { Reaction } from '../entities/reaction'; -import { insertSinglePowerBoosting } from './powerBoostingRepository'; describe('sql injection test cases', sqlInjectionTestCases); @@ -41,11 +39,6 @@ describe( findUsersWhoLikedProjectTestCases, ); -describe( - 'findUsersWhoBoostedProject() testCases', - findUsersWhoBoostedProjectTests, -); - describe( 'findUsersWhoDonatedToProjectExcludeWhoLiked() test cases', findUsersWhoDonatedToProjectTestCases, @@ -110,59 +103,6 @@ function findUsersWhoDonatedToProjectTestCases() { }); } -function findUsersWhoBoostedProjectTests() { - it('should find wallet addresses of who boosted a project', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const thirdUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const fourthUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const project = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: thirdUser, - project, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: fourthUser, - project, - percentage: 0, - }); - - const users = await findUsersWhoBoostedProject(project.id); - assert.equal(users.length, 3); - assert.isOk( - users.find(user => user.walletAddress === firstUser.walletAddress), - ); - assert.isOk( - users.find(user => user.walletAddress === secondUser.walletAddress), - ); - assert.isOk( - users.find(user => user.walletAddress === thirdUser.walletAddress), - ); - assert.isNotOk( - users.find(user => user.walletAddress === fourthUser.walletAddress), - ); - }); -} - function findUsersWhoLikedProjectTestCases() { it('should find wallet addresses of who liked to a project', async () => { const project = await saveProjectDirectlyToDb(createProjectData()); @@ -482,7 +422,7 @@ function sqlInjectionTestCases() { } function findUsersWhoSupportProjectTestCases() { - it('should find wallet addresses of who donated to a project + who liked + who boosted and not having repetitive items - projectOwner', async () => { + it('should find wallet addresses of who donated to a project + who liked and not having repetitive items - projectOwner', async () => { const projectOwner = await saveUserDirectlyToDb( generateRandomEtheriumAddress(), ); @@ -501,15 +441,6 @@ function findUsersWhoSupportProjectTestCases() { const firstUser = await saveUserDirectlyToDb( generateRandomEtheriumAddress(), ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const thirdUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const fourthUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); // Add donors await saveDonationDirectlyToDb(createDonationData(), donor1.id, project.id); @@ -542,30 +473,8 @@ function findUsersWhoSupportProjectTestCases() { reaction: 'heart', }).save(); - // Add power boostings - await insertSinglePowerBoosting({ - user: firstUser, - project, - percentage: 1, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: thirdUser, - project, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: fourthUser, - project, - percentage: 5, - }); - const users = await findUsersWhoSupportProject(project.id); - assert.equal(users.length, 7); + assert.equal(users.length, 4); assert.isOk( users.find(user => user.walletAddress === donor1.walletAddress), ); @@ -575,18 +484,8 @@ function findUsersWhoSupportProjectTestCases() { assert.isOk( users.find(user => user.walletAddress === whoLiked.walletAddress), ); - assert.isOk( users.find(user => user.walletAddress === firstUser.walletAddress), ); - assert.isOk( - users.find(user => user.walletAddress === secondUser.walletAddress), - ); - assert.isOk( - users.find(user => user.walletAddress === thirdUser.walletAddress), - ); - assert.isOk( - users.find(user => user.walletAddress === fourthUser.walletAddress), - ); }); } diff --git a/src/repositories/userRepository.ts b/src/repositories/userRepository.ts index 0f7ca150f..a9fb86155 100644 --- a/src/repositories/userRepository.ts +++ b/src/repositories/userRepository.ts @@ -1,7 +1,6 @@ import { publicSelectionFields, User, UserRole } from '../entities/user'; import { Donation } from '../entities/donation'; import { Reaction } from '../entities/reaction'; -import { PowerBoosting } from '../entities/powerBoosting'; import { Project, ProjStatus, ReviewStatus } from '../entities/project'; import { isEvmAddress } from '../utils/networks'; import { retrieveActiveQfRoundUserMBDScore } from './qfRoundRepository'; @@ -114,26 +113,6 @@ export const createUserWithPublicAddress = async ( }).save(); }; -export const findUsersWhoBoostedProject = async ( - projectId: number, -): Promise<{ walletAddress: string; email?: string }[]> => { - return PowerBoosting.createQueryBuilder('powerBoosting') - .leftJoin('powerBoosting.user', 'user') - .leftJoinAndSelect('powerBoosting.project', 'project') - .leftJoinAndSelect( - User, - 'projectOwner', - 'project.adminUserId = projectOwner.id', - ) - .select('LOWER(user.walletAddress) AS "walletAddress", user.email as email') - .where(`"projectId"=:projectId`, { - projectId, - }) - .andWhere(`percentage > 0`) - .andWhere(`user.id != projectOwner.id`) - .getRawMany(); -}; - export const findUsersWhoLikedProjectExcludeProjectOwner = async ( projectId: number, ): Promise<{ walletAddress: string; email?: string }[]> => { @@ -184,16 +163,13 @@ export const findUsersWhoDonatedToProjectExcludeWhoLiked = async ( export const findUsersWhoSupportProject = async ( projectId: number, ): Promise<{ walletAddress: string; email?: string }[]> => { - const [usersWhoBoosted, usersWhoLiked, usersWhoDonated] = await Promise.all([ - findUsersWhoBoostedProject(projectId), + const [usersWhoLiked, usersWhoDonated] = await Promise.all([ findUsersWhoLikedProjectExcludeProjectOwner(projectId), findUsersWhoDonatedToProjectExcludeWhoLiked(projectId), ]); const users: { walletAddress: string; email?: string }[] = []; - for (const user of usersWhoDonated - .concat(usersWhoLiked) - .concat(usersWhoBoosted)) { + for (const user of usersWhoDonated.concat(usersWhoLiked)) { // Make sure we dont add repetitive users if (!users.find(u => u.walletAddress === user.walletAddress)) { users.push(user); diff --git a/src/resolvers/anchorContractAddressResolver.test.ts b/src/resolvers/anchorContractAddressResolver.test.ts index a53a96818..d2038a92b 100644 --- a/src/resolvers/anchorContractAddressResolver.test.ts +++ b/src/resolvers/anchorContractAddressResolver.test.ts @@ -19,7 +19,7 @@ import { addNewAnchorAddress } from '../repositories/anchorContractAddressReposi import { AnchorContractAddress } from '../entities/anchorContractAddress'; import { findUserByWalletAddress } from '../repositories/userRepository'; -describe( +describe.skip( 'addAnchorContractAddress test cases', addAnchorContractAddressTestCases, ); diff --git a/src/resolvers/anchorContractAddressResolver.ts b/src/resolvers/anchorContractAddressResolver.ts index b45fd4c2b..45aa47462 100644 --- a/src/resolvers/anchorContractAddressResolver.ts +++ b/src/resolvers/anchorContractAddressResolver.ts @@ -15,6 +15,7 @@ import { findUserById } from '../repositories/userRepository'; import { getProvider } from '../provider'; import { logger } from '../utils/logger'; +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => AnchorContractAddress) export class AnchorContractAddressResolver { @Mutation(_returns => AnchorContractAddress, { nullable: true }) diff --git a/src/resolvers/campaignResolver.ts b/src/resolvers/campaignResolver.ts index 1a0f32429..e6c9af862 100644 --- a/src/resolvers/campaignResolver.ts +++ b/src/resolvers/campaignResolver.ts @@ -23,6 +23,7 @@ registerEnumType(CampaignFilterField, { description: 'Same filter fields like projects', }); +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => Campaign) export class CampaignResolver { @Query(_returns => [Campaign], { nullable: true }) diff --git a/src/resolvers/categoryResolver.ts b/src/resolvers/categoryResolver.ts index 6d5c8233b..f3bd35bf5 100644 --- a/src/resolvers/categoryResolver.ts +++ b/src/resolvers/categoryResolver.ts @@ -11,6 +11,7 @@ const qfRoundsAndMainCategoryCacheDuration = (config.get('QF_ROUND_AND_MAIN_CATEGORIES_CACHE_DURATION') as number) || 1000 * 60 * 15; +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => User) export class CategoryResolver { constructor( diff --git a/src/resolvers/chainvineResolver.ts b/src/resolvers/chainvineResolver.ts index 00ef0cef7..9105d76ef 100644 --- a/src/resolvers/chainvineResolver.ts +++ b/src/resolvers/chainvineResolver.ts @@ -10,6 +10,7 @@ import { import { getChainvineAdapter } from '../adapters/adaptersFactory'; import { firstOrCreateReferredEventByUserId } from '../repositories/referredEventRepository'; +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => User) export class ChainvineResolver { @Mutation(_returns => User, { nullable: true }) diff --git a/src/resolvers/donationResolver.test.ts b/src/resolvers/donationResolver.test.ts index 15faa2d43..23304b9c3 100644 --- a/src/resolvers/donationResolver.test.ts +++ b/src/resolvers/donationResolver.test.ts @@ -42,21 +42,10 @@ import { User } from '../entities/user'; import { Organization, ORGANIZATION_LABELS } from '../entities/organization'; import { ProjStatus, ReviewStatus } from '../entities/project'; import { Token } from '../entities/token'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from '../repositories/powerBoostingRepository'; -import { setPowerRound } from '../repositories/powerRoundRepository'; -import { refreshProjectPowerView } from '../repositories/projectPowerViewRepository'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { AppDataSource } from '../orm'; import { generateRandomString } from '../utils/utils'; import { getChainvineAdapter } from '../adapters/adaptersFactory'; import { firstOrCreateReferredEventByUserId } from '../repositories/referredEventRepository'; import { QfRound } from '../entities/qfRound'; -import { addOrUpdatePowerSnapshotBalances } from '../repositories/powerBalanceSnapshotRepository'; -import { findPowerSnapshots } from '../repositories/powerSnapshotRepository'; import { ChainType } from '../types/network'; import { getDefaultSolanaChainId } from '../services/chains'; import { @@ -660,7 +649,7 @@ function donationsTestCases() { allDonationsCount, ); }); - it('should get result with recurring donations joined (for streamed mini donations)', async () => { + it.skip('should get result with recurring donations joined (for streamed mini donations)', async () => { const project = await saveProjectDirectlyToDb(createProjectData()); const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); @@ -1490,83 +1479,6 @@ function createDonationTestCases() { assert.isTrue(donation?.isTokenEligibleForGivback); assert.equal(donation?.amount, amount); }); - it('should create GIV donation and fill averageGivbackFactor', async () => { - const project = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const user = await User.create({ - walletAddress: generateRandomEtheriumAddress(), - loginType: 'wallet', - firstName: 'first name', - }).save(); - - // Clear previous snapshots - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - // Fill ranking and power snapshot - const roundNumber = project.id * 10; - await insertSinglePowerBoosting({ - user, - project, - percentage: 80, - }); - await insertSinglePowerBoosting({ - user, - project: project2, - percentage: 20, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - await setPowerRound(roundNumber); - await refreshProjectPowerView(); - - const accessToken = await generateTestAccessToken(user.id); - const saveDonationResponse = await axios.post( - graphqlUrl, - { - query: createDonationMutation, - variables: { - projectId: project.id, - transactionNetworkId: NETWORK_IDS.XDAI, - transactionId: generateRandomEvmTxHash(), - nonce: 1, - amount: 10, - token: 'GIV', - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isOk(saveDonationResponse.data.data.createDonation); - const donation = await Donation.findOne({ - where: { id: saveDonationResponse.data.data.createDonation }, - }); - - // because this project is rank1 - assert.equal( - donation?.givbackFactor, - Number(process.env.GIVBACK_MAX_FACTOR), - ); - assert.equal(donation?.powerRound, roundNumber); - assert.equal(donation?.projectRank, 1); - }); it('should create GIV donation for giveth project on mainnet successfully', async () => { const project = await saveProjectDirectlyToDb(createProjectData()); const user = await User.create({ diff --git a/src/resolvers/donationResolver.ts b/src/resolvers/donationResolver.ts index 18b9b28dd..5ccc9c12b 100644 --- a/src/resolvers/donationResolver.ts +++ b/src/resolvers/donationResolver.ts @@ -214,6 +214,7 @@ class DonationMetrics { averagePercentageToGiveth: number; } +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => User) export class DonationResolver { private readonly donationRepository: Repository; @@ -268,13 +269,7 @@ export class DonationResolver { .addSelect(publicSelectionFields) .leftJoinAndSelect('donation.project', 'project') .leftJoinAndSelect('donation.recurringDonation', 'recurringDonation') - .leftJoinAndSelect('project.categories', 'categories') - .leftJoin('project.projectPower', 'projectPower') - .addSelect([ - 'projectPower.totalPower', - 'projectPower.powerRank', - 'projectPower.round', - ]); + .leftJoinAndSelect('project.categories', 'categories'); if (fromDate) { query.andWhere(`donation."createdAt" >= '${fromDate}'`); diff --git a/src/resolvers/draftDonationResolver.ts b/src/resolvers/draftDonationResolver.ts index 7d0c585bc..658432cfa 100644 --- a/src/resolvers/draftDonationResolver.ts +++ b/src/resolvers/draftDonationResolver.ts @@ -30,6 +30,7 @@ const draftDonationEnabled = process.env.ENABLE_DRAFT_DONATION === 'true'; const draftRecurringDonationEnabled = process.env.ENABLE_DRAFT_RECURRING_DONATION === 'true'; +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => User) export class DraftDonationResolver { private readonly donationRepository: Repository; diff --git a/src/resolvers/givPowerTestingResolver.ts b/src/resolvers/givPowerTestingResolver.ts deleted file mode 100644 index 3e4424180..000000000 --- a/src/resolvers/givPowerTestingResolver.ts +++ /dev/null @@ -1,315 +0,0 @@ -import { - Arg, - Args, - Field, - Int, - ObjectType, - Query, - Resolver, -} from 'type-graphql'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { findPowerBalances } from '../repositories/powerBalanceSnapshotRepository'; -import { PowerSnapshot } from '../entities/powerSnapshot'; -import { findPowerSnapshots } from '../repositories/powerSnapshotRepository'; -import { PowerRound } from '../entities/powerRound'; -import { getPowerRound } from '../repositories/powerRoundRepository'; -import { ProjectFuturePowerView } from '../views/projectFuturePowerView'; -import { findFuturePowers } from '../repositories/projectFuturePowerViewRepository'; -import { findProjectsPowers } from '../repositories/projectPowerViewRepository'; -import { ProjectPowerView } from '../views/projectPowerView'; -import { UserProjectPowerView } from '../views/userProjectPowerView'; -import { getUserProjectPowers } from '../repositories/userProjectPowerViewRepository'; -import { UserProjectPowerArgs } from './userProjectPowerResolver'; -import { i18n, translationErrorMessagesKeys } from '../utils/errorMessages'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { - findUserPowerBoostings, - findUserProjectPowerBoostingsSnapshots, -} from '../repositories/powerBoostingRepository'; -import { PowerBoosting } from '../entities/powerBoosting'; - -const enableGivPower = process.env.ENABLE_GIV_POWER_TESTING as string; - -@ObjectType() -class PowerBalances { - @Field(_type => [PowerBalanceSnapshot], { nullable: true }) - powerBalances?: PowerBalanceSnapshot[] | undefined; - - @Field(_type => Int) - count: number; -} - -@ObjectType() -class PowerSnapshots { - @Field(_type => [PowerSnapshot], { nullable: true }) - powerSnapshots?: PowerSnapshot[]; - - @Field(_type => Int) - count: number; -} - -@ObjectType() -class FuturePowers { - @Field(_type => [ProjectFuturePowerView], { nullable: true }) - futurePowers?: ProjectFuturePowerView[]; - - @Field(_type => Int) - count: number; -} - -@ObjectType() -class ProjectsPowers { - @Field(_type => [ProjectPowerView], { nullable: true }) - projectsPowers?: ProjectPowerView[]; - - @Field(_type => Int) - count: number; -} - -@ObjectType() -class UserProjectBoostings { - @Field(_type => [UserProjectPowerView], { nullable: true }) - userProjectBoostings?: UserProjectPowerView[]; - - @Field(_type => Int) - count: number; -} - -@ObjectType() -class UserPowerBoostings { - @Field(_type => [PowerBoosting], { nullable: true }) - powerBoostings?: PowerBoosting[]; - - @Field(_type => Int) - count: number; -} - -@ObjectType() -class UserPowerBoostingsSnapshots { - @Field(_type => [PowerBoostingSnapshot], { nullable: true }) - userPowerBoostingsSnapshot?: PowerBoostingSnapshot[]; - - @Field(_type => Int) - count: number; -} - -// General resolver for testing team of givPower -@Resolver() -export class GivPowerTestingResolver { - // Returns powerBalances by userIds or powerSnapshotIds or round, or any combination of those 3 - // Further filtering as required - @Query(_returns => PowerBalances) - async powerBalances( - @Arg('userIds', _type => [Number], { defaultValue: [] }) userIds?: number[], - @Arg('powerSnapshotIds', _type => [Number], { defaultValue: [] }) - powerSnapshotIds?: number[], - @Arg('take', _type => Number, { defaultValue: 100 }) take?: number, - @Arg('skip', _type => Number, { defaultValue: 0 }) skip?: number, - @Arg('round', _type => Number, { nullable: true }) round?: number, - ): Promise { - if (enableGivPower !== 'true') { - return { - powerBalances: [], - count: 0, - }; - } - - const [powerBalances, count] = await findPowerBalances( - round, - userIds, - powerSnapshotIds, - take, - skip, - ); - return { - powerBalances, - count, - }; - } - - // This is so Testing team know what snapshots where taken and in which rounds - // So they can use other endpoints - @Query(_returns => PowerSnapshots) - async powerSnapshots( - @Arg('take', _type => Number, { defaultValue: 100 }) take?: number, - @Arg('skip', _type => Number, { defaultValue: 0 }) skip?: number, - @Arg('round', _type => Number, { nullable: true }) round?: number, - @Arg('powerSnapshotId', _type => Number, { nullable: true }) - powerSnapshotId?: number, - ): Promise { - if (enableGivPower !== 'true') { - return { - powerSnapshots: [], - count: 0, - }; - } - - const [powerSnapshots, count] = await findPowerSnapshots( - round, - powerSnapshotId, - take, - skip, - ); - return { - powerSnapshots, - count, - }; - } - - @Query(_returns => UserPowerBoostings) - async userProjectPowerBoostings( - @Arg('take', _type => Number, { defaultValue: 100 }) take?: number, - @Arg('skip', _type => Number, { defaultValue: 0 }) skip?: number, - @Arg('projectId', _type => Number, { nullable: true }) projectId?: number, - @Arg('userId', _type => Number, { nullable: true }) userId?: number, - ): Promise { - if (enableGivPower !== 'true') { - return { - powerBoostings: [], - count: 0, - }; - } - - const [powerBoostings, count] = await findUserPowerBoostings( - userId, - projectId, - take, - skip, - ); - - return { - powerBoostings, - count, - }; - } - - @Query(_returns => UserPowerBoostingsSnapshots) - async userProjectPowerBoostingsSnapshots( - @Arg('take', _type => Number, { defaultValue: 100 }) take?: number, - @Arg('skip', _type => Number, { defaultValue: 0 }) skip?: number, - @Arg('projectId', _type => Number, { nullable: true }) projectId?: number, - @Arg('userId', _type => Number, { nullable: true }) userId?: number, - @Arg('powerSnapshotId', _type => Number, { nullable: true }) - powerSnapshotId?: number, - @Arg('round', _type => Number, { nullable: true }) round?: number, - ): Promise { - if (enableGivPower !== 'true') { - return { - userPowerBoostingsSnapshot: [], - count: 0, - }; - } - - const [userPowerBoostingsSnapshot, count] = - await findUserProjectPowerBoostingsSnapshots( - userId, - projectId, - take, - skip, - powerSnapshotId, - round, - ); - - return { - userPowerBoostingsSnapshot, - count, - }; - } - - // Know the current round running - @Query(_returns => PowerRound) - async currentPowerRound(): Promise { - return getPowerRound(); - } - - @Query(_returns => FuturePowers) - async projectsFuturePowers( - @Arg('projectIds', _type => [Number], { defaultValue: [] }) - projectIds?: number[], - @Arg('round', _type => Number, { nullable: true }) round?: number, - @Arg('take', _type => Number, { defaultValue: 100 }) take?: number, - @Arg('skip', _type => Number, { defaultValue: 0 }) skip?: number, - ): Promise { - if (enableGivPower !== 'true') { - return { - futurePowers: [], - count: 0, - }; - } - - const [futurePowers, count] = await findFuturePowers( - projectIds, - round, - take, - skip, - ); - - return { - futurePowers, - count, - }; - } - - @Query(_returns => ProjectsPowers) - async projectsPowers( - @Arg('projectIds', _type => [Number], { defaultValue: [] }) - projectIds?: number[], - @Arg('round', _type => Number, { nullable: true }) round?: number, - @Arg('take', _type => Number, { defaultValue: 100 }) take?: number, - @Arg('skip', _type => Number, { defaultValue: 0 }) skip?: number, - ): Promise { - if (enableGivPower !== 'true') { - return { - projectsPowers: [], - count: 0, - }; - } - - const [projectsPowers, count] = await findProjectsPowers( - projectIds, - round, - take, - skip, - ); - - return { - projectsPowers, - count, - }; - } - - @Query(_returns => UserProjectBoostings) - async userProjectBoostings( - @Args() - { take, skip, projectId, userId, orderBy, round }: UserProjectPowerArgs, - ): Promise { - if (enableGivPower !== 'true') { - return { - userProjectBoostings: [], - count: 0, - }; - } - - if (!projectId && !userId) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.SHOULD_SEND_AT_LEAST_ONE_OF_PROJECT_ID_AND_USER_ID, - ), - ); - } - - const [userProjectBoostings, count] = await getUserProjectPowers({ - take, - skip, - orderBy, - userId, - projectId, - round, - }); - - return { - userProjectBoostings, - count, - }; - } -} diff --git a/src/resolvers/instantPowerResolver.test.ts b/src/resolvers/instantPowerResolver.test.ts deleted file mode 100644 index dd17d325c..000000000 --- a/src/resolvers/instantPowerResolver.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { expect } from 'chai'; -import axios from 'axios'; -import { - refreshProjectUserInstantPowerView, - saveOrUpdateInstantPowerBalances, -} from '../repositories/instantBoostingRepository'; -import { InstantPowerBalance } from '../entities/instantPowerBalance'; -import { - createProjectData, - generateRandomEtheriumAddress, - graphqlUrl, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, -} from '../../test/testUtils'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { insertSinglePowerBoosting } from './../repositories/powerBoostingRepository'; -import { getProjectUserInstantPowerQuery } from '../../test/graphqlQueries'; -import { User } from '../entities/user'; -import { Project } from '../entities/project'; - -describe( - 'projectUserInstantPowerView test cases', - projectUserInstantPowerViewTest, -); - -function projectUserInstantPowerViewTest() { - let project1: Project; - let project2: Project; - let project3: Project; - - let user1: User; - let user2: User; - let user3: User; - let user4: User; - - before(async () => { - await PowerBoosting.clear(); - await InstantPowerBalance.clear(); - - project1 = await saveProjectDirectlyToDb(createProjectData()); - project2 = await saveProjectDirectlyToDb(createProjectData()); - project3 = await saveProjectDirectlyToDb(createProjectData()); - user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - user3 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - user4 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - await saveOrUpdateInstantPowerBalances([ - { - userId: user1.id, - balanceAggregatorUpdatedAt: new Date(100_000), - balance: 1000, - }, - { - userId: user2.id, - balanceAggregatorUpdatedAt: new Date(200_000), - balance: 2000, - }, - { - userId: user3.id, - balanceAggregatorUpdatedAt: new Date(300_000), - balance: 3000, - }, - { - userId: user4.id, - balanceAggregatorUpdatedAt: new Date(400_000), - balance: 4000, - }, - ]); - - await Promise.all( - [ - // user 1 - { user: user1, project: project1, percentage: 50 }, // 500 - { user: user1, project: project2, percentage: 30 }, // 300 - { user: user1, project: project3, percentage: 20 }, // 200 - - // user 2 - { user: user2, project: project1, percentage: 10 }, // 200 - { user: user2, project: project2, percentage: 70 }, // 1400 - { user: user2, project: project3, percentage: 20 }, // 400 - - // user 3 - { user: user3, project: project1, percentage: 80 }, // 2400 - { user: user3, project: project2, percentage: 5 }, // 150 - { user: user3, project: project3, percentage: 15 }, // 450 - - // user 4 - { user: user4, project: project2, percentage: 80 }, // 3200 - { user: user4, project: project3, percentage: 20 }, // 800 - ].map(insertSinglePowerBoosting), - ); - - await refreshProjectUserInstantPowerView(); - }); - it('should return correct result ordered', async () => { - const expectedResults = [ - { - projectId: project1.id, - expectedResult: [ - { userId: user3.id, boostedPower: 2400 }, - { userId: user1.id, boostedPower: 500 }, - { userId: user2.id, boostedPower: 200 }, - ], - }, - { - projectId: project2.id, - expectedResult: [ - { userId: user4.id, boostedPower: 3200 }, - { userId: user2.id, boostedPower: 1400 }, - { userId: user1.id, boostedPower: 300 }, - { userId: user3.id, boostedPower: 150 }, - ], - }, - { - projectId: project3.id, - expectedResult: [ - { userId: user4.id, boostedPower: 800 }, - { userId: user3.id, boostedPower: 450 }, - { userId: user2.id, boostedPower: 400 }, - { userId: user1.id, boostedPower: 200 }, - ], - }, - ]; - - for (const { projectId, expectedResult } of expectedResults) { - const result = await axios.post(graphqlUrl, { - query: getProjectUserInstantPowerQuery, - variables: { projectId: +projectId, take: 10, skip: 0 }, - }); - const { projectUserInstantPowers, total } = - result.data.data.getProjectUserInstantPower; - expect(total).equal(expectedResult.length); - expect(projectUserInstantPowers).length(expectedResult.length); - expect( - projectUserInstantPowers.map(({ userId, boostedPower }) => { - return { userId, boostedPower }; - }), - ).deep.equal(expectedResult); - } - }); - - it('should return correct result ordered with pagination', async () => { - const result = await axios.post(graphqlUrl, { - query: getProjectUserInstantPowerQuery, - variables: { projectId: +project3.id, take: 2, skip: 0 }, - }); - const { projectUserInstantPowers, total } = - result.data.data.getProjectUserInstantPower; - expect(total).equal(4); - expect(projectUserInstantPowers).length(2); - expect( - projectUserInstantPowers.map(({ userId, boostedPower }) => { - return { userId, boostedPower }; - }), - ).deep.equal([ - { userId: user4.id, boostedPower: 800 }, - { userId: user3.id, boostedPower: 450 }, - ]); - - const result2 = await axios.post(graphqlUrl, { - query: getProjectUserInstantPowerQuery, - variables: { projectId: +project3.id, take: 2, skip: 2 }, - }); - const { - projectUserInstantPowers: projectUserInstantPowers2, - total: total2, - } = result2.data.data.getProjectUserInstantPower; - expect(total2).equal(4); - expect(projectUserInstantPowers2).length(2); - expect( - projectUserInstantPowers2.map(({ userId, boostedPower }) => { - return { userId, boostedPower }; - }), - ).deep.equal([ - { userId: user2.id, boostedPower: 400 }, - { userId: user1.id, boostedPower: 200 }, - ]); - }); - - it('should return user information correctly', async () => { - user1.avatar = 'avatar1'; - user2.avatar = 'avatar2'; - user3.avatar = 'avatar3'; - user4.avatar = 'avatar4'; - - await User.save([user1, user2, user3, user4]); - - const result = await axios.post(graphqlUrl, { - query: getProjectUserInstantPowerQuery, - variables: { projectId: +project3.id }, - }); - - const { projectUserInstantPowers } = - result.data.data.getProjectUserInstantPower; - - const expectedUsers = [user4, user3, user2, user1]; - expect(projectUserInstantPowers.map(({ user }) => user)).deep.equal( - expectedUsers.map(({ name, walletAddress, avatar }) => ({ - name, - walletAddress, - avatar, - })), - ); - }); -} diff --git a/src/resolvers/instantPowerResolver.ts b/src/resolvers/instantPowerResolver.ts deleted file mode 100644 index 1cb826de6..000000000 --- a/src/resolvers/instantPowerResolver.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Arg, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'; - -import { ProjectUserInstantPowerView } from '../views/projectUserInstantPowerView'; -import { getProjectUserInstantPowerView } from '../repositories/instantBoostingRepository'; - -@ObjectType() -class PaginatedProjectUserInstantPowerView { - @Field(() => [ProjectUserInstantPowerView]) - projectUserInstantPowers: ProjectUserInstantPowerView[]; - - @Field(() => Int) - total: number; -} -@Resolver() -export class ProjectUserInstantPowerViewResolver { - @Query(() => PaginatedProjectUserInstantPowerView, { nullable: true }) - async getProjectUserInstantPower( - @Arg('projectId', () => Int, { nullable: false }) projectId: number, - @Arg('take', _type => Int, { nullable: true }) take?: number, - @Arg('skip', _type => Int, { defaultValue: 0 }) skip?: number, - ): Promise { - const [projectUserInstantPowers, total] = - await getProjectUserInstantPowerView(projectId, take, skip); - return { - projectUserInstantPowers, - total, - }; - } -} diff --git a/src/resolvers/powerBoostingResolver.test.ts b/src/resolvers/powerBoostingResolver.test.ts deleted file mode 100644 index 85e8041d0..000000000 --- a/src/resolvers/powerBoostingResolver.test.ts +++ /dev/null @@ -1,1178 +0,0 @@ -import axios, { AxiosResponse } from 'axios'; -import { assert } from 'chai'; -import { - createProjectData, - generateRandomEtheriumAddress, - generateTestAccessToken, - graphqlUrl, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, - SEED_DATA, -} from '../../test/testUtils'; -import { - getBottomPowerRankQuery, - getPowerBoostingsQuery, - setMultiplePowerBoostingMutation, - setSinglePowerBoostingMutation, -} from '../../test/graphqlQueries'; -import { errorMessages } from '../utils/errorMessages'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from '../repositories/powerBoostingRepository'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { setPowerRound } from '../repositories/powerRoundRepository'; -import { refreshProjectPowerView } from '../repositories/projectPowerViewRepository'; -import { AppDataSource } from '../orm'; -import { addOrUpdatePowerSnapshotBalances } from '../repositories/powerBalanceSnapshotRepository'; -import { findPowerSnapshots } from '../repositories/powerSnapshotRepository'; - -describe( - 'setSinglePowerBoostingMutation test cases', - setSinglePowerBoostingTestCases, -); - -describe( - 'setMultiplePowerBoostingMutation test cases', - setMultiplePowerBoostingTestCases, -); -describe('getPowerBoosting test cases', getPowerBoostingTestCases); -describe('getBottomPowerRank test cases', getBottomPowerRankTestCases); - -// Clean percentages after setting -const removePowerBoostings = async (boosts: PowerBoosting[]): Promise => { - await PowerBoosting.delete(boosts.map(b => b.id)); -}; - -const sendSingleBoostQuery = async ( - userId: number, - projectId: number, - percentage: number, -): Promise => { - const accessToken = await generateTestAccessToken(userId); - return axios.post( - graphqlUrl, - { - query: setSinglePowerBoostingMutation, - variables: { - projectId, - percentage, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); -}; - -const sendMultipleBoostQuery = async ( - userId: number, - projectIds: number[], - percentages: number[], -): Promise => { - const accessToken = await generateTestAccessToken(userId); - return axios.post( - graphqlUrl, - { - query: setMultiplePowerBoostingMutation, - variables: { - projectIds, - percentages, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); -}; - -function setSinglePowerBoostingTestCases() { - it('should get error when the user is not authenticated', async () => { - const result = await axios.post(graphqlUrl, { - query: setSinglePowerBoostingMutation, - variables: { - projectId: SEED_DATA.FIRST_PROJECT.id, - percentage: 100, - }, - }); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.AUTHENTICATION_REQUIRED, - ); - }); - - it('should get error when the user wants to boost a project with invalid percentage value', async () => { - let result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - 101, - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - - result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - -1, - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - }); - - it('should get error when the user boosts for the first time with a value other than 100%', async () => { - const result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - 90, - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_FIRST_PROJECT_100_PERCENT, - ); - }); - - it('should get error when the user with single boosted project boosts it with a value other than 100%', async () => { - let result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - 100, - ); - assert.isOk(result); - const powerBoostings: PowerBoosting[] = - result.data.data.setSinglePowerBoosting; - - result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - 90, - ); - - assert.isOk(result); - - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_FIRST_PROJECT_100_PERCENT, - ); - - // clean - await removePowerBoostings(powerBoostings); - }); - - it('should set single project boost percentage 100%', async () => { - const result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - 100, - ); - - assert.isOk(result); - const powerBoostings: PowerBoosting[] = - result.data.data.setSinglePowerBoosting; - assert.isArray(powerBoostings); - assert.lengthOf(powerBoostings, 1); - assert.equal(powerBoostings[0].percentage, 100); - - // Clean - await removePowerBoostings(powerBoostings); - }); - - it('should adjust older boosting by setting a single boosting', async () => { - let result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - 100, - ); - - let powerBoostings: PowerBoosting[] = - result.data.data.setSinglePowerBoosting; - - // Boost the second project 20 percent - result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.SECOND_PROJECT.id, - 20, - ); - - powerBoostings = result.data.data.setSinglePowerBoosting; - - assert.lengthOf(powerBoostings, 2); - - let firstProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.FIRST_PROJECT.id, - ) as PowerBoosting; - let secondProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.SECOND_PROJECT.id, - ) as PowerBoosting; - - assert.isDefined(firstProjectBoost); - assert.isDefined(secondProjectBoost); - - assert.equal(firstProjectBoost.percentage, 80); - assert.equal(secondProjectBoost.percentage, 20); - - // Third project 40 percent - result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.TRANSAK_PROJECT.id, - 40, - ); - - powerBoostings = result.data.data.setSinglePowerBoosting; - - assert.lengthOf(powerBoostings, 3); - - firstProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.FIRST_PROJECT.id, - ) as PowerBoosting; - secondProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.SECOND_PROJECT.id, - ) as PowerBoosting; - const thirdProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.TRANSAK_PROJECT.id, - ) as PowerBoosting; - - assert.isDefined(firstProjectBoost); - assert.isDefined(secondProjectBoost); - assert.isDefined(thirdProjectBoost); - - assert.equal(firstProjectBoost.percentage, 48); - assert.equal(secondProjectBoost.percentage, 12); - assert.equal(thirdProjectBoost.percentage, 40); - - // Clean - await removePowerBoostings(powerBoostings); - }); - - it('should remove older boosting by setting a single 100% boosting', async () => { - await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - 100, - ); - - await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.SECOND_PROJECT.id, - 20, - ); - - // Third project 40 percent - const result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.TRANSAK_PROJECT.id, - 100, - ); - - const powerBoostings = result.data.data.setSinglePowerBoosting; - - assert.lengthOf(powerBoostings, 1); - - const thirdProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.TRANSAK_PROJECT.id, - ) as PowerBoosting; - - assert.isDefined(thirdProjectBoost); - - assert.equal(thirdProjectBoost.percentage, 100); - - // Clean - await removePowerBoostings(powerBoostings); - }); - - it('should update boosting by setting a single boosting', async () => { - await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - 100, - ); - - await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.SECOND_PROJECT.id, - 20, - ); - - const result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - 40, - ); - - const powerBoostings = result.data.data.setSinglePowerBoosting; - - assert.lengthOf(powerBoostings, 2); - - const firstProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.FIRST_PROJECT.id, - ) as PowerBoosting; - const secondProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.SECOND_PROJECT.id, - ) as PowerBoosting; - - assert.isDefined(firstProjectBoost); - assert.isDefined(secondProjectBoost); - - assert.equal(firstProjectBoost.percentage, 40); - assert.equal(secondProjectBoost.percentage, 60); - - // Clean - await removePowerBoostings(powerBoostings); - }); - - // GIVPOWER_BOOSTING_USER_PROJECTS_LIMIT=5 - it('should get error when number of boosted projects will be more than limit', async () => { - await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIRST_PROJECT.id, - 100, - ); - await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.SECOND_PROJECT.id, - 10, - ); - await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.TRANSAK_PROJECT.id, - 10, - ); - await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FOURTH_PROJECT.id, - 10, - ); - let result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIFTH_PROJECT.id, - 10, - ); - - assert.isOk(result); - let powerBoostings = result?.data?.data?.setSinglePowerBoosting; - assert.lengthOf(powerBoostings, 5); - - /// Must exceed limit - result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.SIXTH_PROJECT.id, - 10, - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_MAX_PROJECT_LIMIT, - ); - - /// Zero one of previous boostings - result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIFTH_PROJECT.id, - 0, - ); - assert.isOk(result); - powerBoostings = result?.data?.data?.setSinglePowerBoosting; - assert.lengthOf(powerBoostings, 4); - - /// Must be able to boost the sixth project now! - result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.SIXTH_PROJECT.id, - 30, - ); - assert.isOk(result); - powerBoostings = result?.data?.data?.setSinglePowerBoosting; - assert.lengthOf(powerBoostings, 5); - - // Set fifth project 100% - - result = await sendSingleBoostQuery( - SEED_DATA.FIRST_USER.id, - SEED_DATA.FIFTH_PROJECT.id, - 100, - ); - assert.isOk(result); - powerBoostings = result?.data?.data?.setSinglePowerBoosting; - assert.lengthOf(powerBoostings, 1); - - await removePowerBoostings(powerBoostings); - }); -} - -function setMultiplePowerBoostingTestCases() { - it('should get error when the user is not authenticated', async () => { - const result = await axios.post(graphqlUrl, { - query: setMultiplePowerBoostingMutation, - variables: { - projectIds: [SEED_DATA.FIRST_PROJECT.id], - percentages: [100], - }, - }); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.AUTHENTICATION_REQUIRED, - ); - }); - - it('should get error when the user wants to boost a project with invalid percentage value', async () => { - let result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [SEED_DATA.FIRST_PROJECT.id, SEED_DATA.SECOND_PROJECT.id], - [101, 3], - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - - result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [SEED_DATA.FIRST_PROJECT.id, SEED_DATA.SECOND_PROJECT.id], - [20, -1], - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - }); - - it('should get error when the user wants to boost with invalid arrays of projectIds and percentages', async () => { - // Empty projectIds and percentages arrays - let result = await sendMultipleBoostQuery(SEED_DATA.FIRST_USER.id, [], []); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - - // Longer projectIds array - result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [SEED_DATA.FIRST_PROJECT.id, SEED_DATA.SECOND_PROJECT.id], - [100], - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - - // Longer percentages array - result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [SEED_DATA.FIRST_PROJECT.id, SEED_DATA.SECOND_PROJECT.id], - [20, 30, 50], - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - - // Repeat a project - result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [ - SEED_DATA.FIRST_PROJECT.id, - SEED_DATA.SECOND_PROJECT.id, - SEED_DATA.FIRST_PROJECT.id, - ], - [20, 30, 50], - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - - // Invalid sum #1 - result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [ - SEED_DATA.FIRST_PROJECT.id, - SEED_DATA.SECOND_PROJECT.id, - SEED_DATA.TRANSAK_PROJECT.id, - ], - [20, 30, 49], // Less than 100 - (0.01 * number of projects) - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - // Invalid sum #2 - result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [ - SEED_DATA.FIRST_PROJECT.id, - SEED_DATA.SECOND_PROJECT.id, - SEED_DATA.TRANSAK_PROJECT.id, - ], - [20, 33, 49], // More than 100 - (0.01 * number of projects) - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - - // Invalid #3 - set less than 100 - (0.01 * number of projects) - result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [ - SEED_DATA.FIRST_PROJECT.id, - SEED_DATA.SECOND_PROJECT.id, - SEED_DATA.TRANSAK_PROJECT.id, - ], - [5, 12, 82.96], - ); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.ERROR_GIVPOWER_BOOSTING_INVALID_DATA, - ); - }); - - it('should set multiple power boosting with correct data', async () => { - let result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [ - SEED_DATA.FIRST_PROJECT.id, - SEED_DATA.SECOND_PROJECT.id, - SEED_DATA.TRANSAK_PROJECT.id, - ], - [20, 33, 47], - ); - - assert.isOk(result); - let powerBoostings = result.data.data.setMultiplePowerBoosting; - - assert.lengthOf(powerBoostings, 3); - - const firstProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.FIRST_PROJECT.id, - ) as PowerBoosting; - const secondProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.SECOND_PROJECT.id, - ) as PowerBoosting; - const thirdProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.TRANSAK_PROJECT.id, - ) as PowerBoosting; - - assert.isDefined(firstProjectBoost); - assert.isDefined(secondProjectBoost); - assert.isDefined(thirdProjectBoost); - - assert.equal(firstProjectBoost.percentage, 20); - assert.equal(secondProjectBoost.percentage, 33); - assert.equal(thirdProjectBoost.percentage, 47); - - result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [ - SEED_DATA.FOURTH_PROJECT.id, - SEED_DATA.FIFTH_PROJECT.id, - SEED_DATA.SIXTH_PROJECT.id, - ], - [19.99, 49.99, 29.99], - ); - - assert.isOk(result); - powerBoostings = result.data.data.setMultiplePowerBoosting; - - assert.lengthOf(powerBoostings, 3); - - const fourthProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.FOURTH_PROJECT.id, - ) as PowerBoosting; - const fifthProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.FIFTH_PROJECT.id, - ) as PowerBoosting; - const sixthProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.SIXTH_PROJECT.id, - ) as PowerBoosting; - - assert.isDefined(fourthProjectBoost); - assert.isDefined(fifthProjectBoost); - assert.isDefined(sixthProjectBoost); - - assert.equal(fourthProjectBoost.percentage, 19.99); - assert.equal(fifthProjectBoost.percentage, 49.99); - assert.equal(sixthProjectBoost.percentage, 29.99); - - await removePowerBoostings(powerBoostings); - }); - - it('should clear/override/create power boostings', async () => { - await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [ - SEED_DATA.FIRST_PROJECT.id, - SEED_DATA.SECOND_PROJECT.id, - SEED_DATA.TRANSAK_PROJECT.id, - ], - [40, 30, 30], - ); - - const result = await sendMultipleBoostQuery( - SEED_DATA.FIRST_USER.id, - [ - SEED_DATA.FIRST_PROJECT.id, // Override - SEED_DATA.SECOND_PROJECT.id, // Clear - SEED_DATA.FOURTH_PROJECT.id, // Create - ], - [50, 0, 50], - ); - - assert.isOk(result); - const powerBoostings = result.data.data.setMultiplePowerBoosting; - - assert.lengthOf(powerBoostings, 2); - - const firstProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.FIRST_PROJECT.id, - ) as PowerBoosting; - const fourthProjectBoost = powerBoostings.find( - pb => +pb.project.id === SEED_DATA.FOURTH_PROJECT.id, - ) as PowerBoosting; - - assert.isDefined(firstProjectBoost); - assert.isDefined(fourthProjectBoost); - - assert.equal(firstProjectBoost.percentage, 50); - assert.equal(fourthProjectBoost.percentage, 50); - - await removePowerBoostings(powerBoostings); - }); -} - -function getPowerBoostingTestCases() { - it('should get error when the user doesnt send nether projectId nor userId', async () => { - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: {}, - }); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.SHOULD_SEND_AT_LEAST_ONE_OF_PROJECT_ID_AND_USER_ID, - ); - }); - it('should get list of power boostings filter by userId', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - userId: firstUser.id, - }, - }); - assert.isOk(result); - assert.equal(result.data.data.getPowerBoosting.powerBoostings.length, 2); - result.data.data.getPowerBoosting.powerBoostings.forEach(powerBoosting => { - assert.equal(powerBoosting.user.id, firstUser.id); - }); - }); - it('should get list of power boostings filter by projectId', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 3, - }); - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - projectId: firstProject.id, - }, - }); - assert.isOk(result); - assert.equal(result.data.data.getPowerBoosting.powerBoostings.length, 2); - - result.data.data.getPowerBoosting.powerBoostings.forEach(powerBoosting => { - assert.equal(powerBoosting.project.id, firstProject.id); - }); - }); - it('should get list of power with 1000 boostings filter by projectId', async () => { - await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project = await saveProjectDirectlyToDb(createProjectData()); - for (let i = 0; i < 1000; i++) { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - await insertSinglePowerBoosting({ - user, - project, - percentage: 100, - }); - } - - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - projectId: project.id, - }, - }); - assert.isOk(result); - assert.equal(result.data.data.getPowerBoosting.powerBoostings.length, 1000); - - result.data.data.getPowerBoosting.powerBoostings.forEach(powerBoosting => { - assert.equal(powerBoosting.project.id, project.id); - }); - }); - it('should get list of power boostings filter by projectId and userId', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 3, - }); - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - projectId: firstProject.id, - userId: firstUser.id, - }, - }); - assert.isOk(result); - assert.equal(result.data.data.getPowerBoosting.powerBoostings.length, 1); - - result.data.data.getPowerBoosting.powerBoostings.forEach(powerBoosting => { - assert.equal(powerBoosting.project.id, firstProject.id); - assert.equal(powerBoosting.user.id, firstUser.id); - }); - }); - it('should get list of power boostings filter by projectId and userId, should not send user email in response', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 3, - }); - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - projectId: firstProject.id, - }, - }); - assert.isOk(result); - result.data.data.getPowerBoosting.powerBoostings.forEach(powerBoosting => { - assert.isNotOk(powerBoosting.user.email); - }); - }); - - it('should get list of power boostings filter by userId, sort by updatedAt DESC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: thirdProject, - percentage: 3, - }); - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - userId: firstUser.id, - orderBy: { - field: 'UpdatedAt', - direction: 'DESC', - }, - }, - }); - assert.isOk(result); - const powerBoostings = result.data.data.getPowerBoosting.powerBoostings; - assert.equal(powerBoostings.length, 3); - assert.isTrue(powerBoostings[0].updatedAt >= powerBoostings[1].updatedAt); - assert.isTrue(powerBoostings[1].updatedAt >= powerBoostings[2].updatedAt); - }); - it('should get list of power boostings filter by userId, sort by updatedAt ASC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: thirdProject, - percentage: 3, - }); - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - userId: firstUser.id, - orderBy: { - field: 'UpdatedAt', - direction: 'ASC', - }, - }, - }); - assert.isOk(result); - const powerBoostings = result.data.data.getPowerBoosting.powerBoostings; - assert.equal(powerBoostings.length, 3); - assert.isTrue(powerBoostings[0].updatedAt <= powerBoostings[1].updatedAt); - assert.isTrue(powerBoostings[1].updatedAt <= powerBoostings[2].updatedAt); - }); - - it('should get list of power boostings filter by userId, sort by createdAt DESC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: thirdProject, - percentage: 3, - }); - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - userId: firstUser.id, - orderBy: { - field: 'CreationAt', - direction: 'DESC', - }, - }, - }); - assert.isOk(result); - const powerBoostings = result.data.data.getPowerBoosting.powerBoostings; - assert.equal(powerBoostings.length, 3); - assert.isTrue(powerBoostings[0].createdAt >= powerBoostings[1].createdAt); - assert.isTrue(powerBoostings[1].createdAt >= powerBoostings[2].createdAt); - }); - it('should get list of power boostings filter by userId, sort by createdAt ASC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: thirdProject, - percentage: 3, - }); - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - userId: firstUser.id, - orderBy: { - field: 'CreationAt', - direction: 'ASC', - }, - }, - }); - assert.isOk(result); - const powerBoostings = result.data.data.getPowerBoosting.powerBoostings; - assert.equal(powerBoostings.length, 3); - assert.isTrue(powerBoostings[0].createdAt <= powerBoostings[1].createdAt); - assert.isTrue(powerBoostings[1].createdAt <= powerBoostings[2].createdAt); - }); - - it('should get list of power boostings filter by userId, sort by percentage DESC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: thirdProject, - percentage: 3, - }); - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - userId: firstUser.id, - orderBy: { - field: 'Percentage', - direction: 'DESC', - }, - }, - }); - assert.isOk(result); - const powerBoostings = result.data.data.getPowerBoosting.powerBoostings; - assert.equal(powerBoostings.length, 3); - assert.isTrue(powerBoostings[0].percentage >= powerBoostings[1].percentage); - assert.isTrue(powerBoostings[1].percentage >= powerBoostings[2].percentage); - }); - it('should get list of power boostings filter by userId, sort by percentage ASC', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: thirdProject, - percentage: 3, - }); - const result = await axios.post(graphqlUrl, { - query: getPowerBoostingsQuery, - variables: { - userId: firstUser.id, - orderBy: { - field: 'Percentage', - direction: 'ASC', - }, - }, - }); - assert.isOk(result); - const powerBoostings = result.data.data.getPowerBoosting.powerBoostings; - assert.equal(powerBoostings.length, 3); - assert.isTrue(powerBoostings[0].percentage <= powerBoostings[1].percentage); - assert.isTrue(powerBoostings[1].percentage <= powerBoostings[2].percentage); - }); -} -async function getBottomPowerRankTestCases() { - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - it('Get last power rank', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: thirdProject, - percentage: 15, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - const roundNumber = firstProject.id * 10; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: firstUser.id, - powerSnapshotId: snapshot.id, - balance: 100, - }); - - await setPowerRound(roundNumber); - await refreshProjectPowerView(); - - const result = await axios.post(graphqlUrl, { - query: getBottomPowerRankQuery, - }); - assert.isOk(result); - assert.equal(result.data.data.getTopPowerRank, 4); - }); -} diff --git a/src/resolvers/powerBoostingResolver.ts b/src/resolvers/powerBoostingResolver.ts deleted file mode 100644 index 3feb19638..000000000 --- a/src/resolvers/powerBoostingResolver.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { - Arg, - Args, - ArgsType, - Ctx, - Field, - Float, - InputType, - Int, - Mutation, - ObjectType, - Query, - registerEnumType, - Resolver, -} from 'type-graphql'; -import { Max, Min } from 'class-validator'; -import { Service } from 'typedi'; -import { ApolloContext } from '../types/ApolloContext'; -import { i18n, translationErrorMessagesKeys } from '../utils/errorMessages'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { - setMultipleBoosting, - setSingleBoosting, - findPowerBoostings, -} from '../repositories/powerBoostingRepository'; -import { logger } from '../utils/logger'; -import { getBottomRank } from '../repositories/projectPowerViewRepository'; -import { getNotificationAdapter } from '../adapters/adaptersFactory'; - -enum PowerBoostingOrderDirection { - ASC = 'ASC', - DESC = 'DESC', -} - -enum PowerBoostingOrderField { - CreationAt = 'createdAt', - UpdatedAt = 'updatedAt', - Percentage = 'percentage', -} - -registerEnumType(PowerBoostingOrderField, { - name: 'PowerBoostingOrderField', - description: 'Order by field', -}); - -registerEnumType(PowerBoostingOrderDirection, { - name: 'PowerBoostingOrderDirection', - description: 'Order direction', -}); - -@InputType() -class PowerBoostingOrderBy { - @Field(_type => PowerBoostingOrderField, { - nullable: true, - defaultValue: PowerBoostingOrderField.UpdatedAt, - }) - field: PowerBoostingOrderField; - - @Field(_type => PowerBoostingOrderDirection, { - nullable: true, - defaultValue: PowerBoostingOrderDirection.DESC, - }) - direction: PowerBoostingOrderDirection; -} - -@Service() -@ArgsType() -export class GetPowerBoostingArgs { - @Field(_type => Int, { defaultValue: 0 }) - @Min(0) - skip: number; - - @Field(_type => Int, { defaultValue: 1000 }) - @Min(0) - @Max(1000) - take: number; - - @Field(_type => PowerBoostingOrderBy, { - nullable: true, - defaultValue: { - field: PowerBoostingOrderField.UpdatedAt, - direction: PowerBoostingOrderDirection.DESC, - }, - }) - orderBy: PowerBoostingOrderBy; - - @Field(_type => Int, { nullable: true }) - projectId?: number; - - @Field(_type => Int, { nullable: true }) - userId?: number; -} - -@ObjectType() -class GivPowers { - @Field(_type => [PowerBoosting]) - powerBoostings: PowerBoosting[]; - - @Field(_type => Int) - totalCount: number; -} - -@Resolver(_of => PowerBoosting) -export class PowerBoostingResolver { - @Mutation(_returns => [PowerBoosting]) - async setMultiplePowerBoosting( - @Arg('projectIds', _type => [Int]) projectIds: number[], - @Arg('percentages', _type => [Float]) percentages: number[], - @Ctx() { req: { user } }: ApolloContext, - ): Promise { - const userId = user?.userId; - if (!user || !userId) { - throw new Error( - i18n.__(translationErrorMessagesKeys.AUTHENTICATION_REQUIRED), - ); - } - - // validator: sum of percentages should not be more than 100, all projects should active, ... - const result = await setMultipleBoosting({ - userId, - projectIds, - percentages, - }); - - await getNotificationAdapter().projectBoostedBatch({ - userId, - projectIds, - }); - return result; - } - - @Mutation(_returns => [PowerBoosting]) - async setSinglePowerBoosting( - @Arg('projectId', _type => Int) projectId: number, - @Arg('percentage', _type => Float) percentage: number, - @Ctx() { req: { user } }: ApolloContext, - ): Promise { - const userId = user?.userId; - if (!user || !userId) { - throw new Error( - i18n.__(translationErrorMessagesKeys.AUTHENTICATION_REQUIRED), - ); - } - - const result = await setSingleBoosting({ - userId, - projectId, - percentage, - }); - getNotificationAdapter() - .projectBoosted({ projectId, userId }) - .catch(err => - logger.error('send projectBoosted notification error', err), - ); - return result; - } - - @Query(_returns => GivPowers) - async getPowerBoosting( - @Args() - { take, skip, projectId, userId, orderBy }: GetPowerBoostingArgs, - ): Promise { - if (!projectId && !userId) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.SHOULD_SEND_AT_LEAST_ONE_OF_PROJECT_ID_AND_USER_ID, - ), - ); - } - const [powerBoostings, totalCount] = await findPowerBoostings({ - userId, - projectId, - skip, - orderBy, - take, - }); - return { - powerBoostings, - totalCount, - }; - } - - @Query(_returns => Number) - async getTopPowerRank(): Promise { - return getBottomRank(); - } -} diff --git a/src/resolvers/projectPowerResolver.test.ts b/src/resolvers/projectPowerResolver.test.ts deleted file mode 100644 index bd7045f72..000000000 --- a/src/resolvers/projectPowerResolver.test.ts +++ /dev/null @@ -1,403 +0,0 @@ -import axios from 'axios'; -import { assert } from 'chai'; -import { - createProjectData, - generateRandomEtheriumAddress, - graphqlUrl, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, -} from '../../test/testUtils'; -import { getPowerAmountRankQuery } from '../../test/graphqlQueries'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { - refreshProjectFuturePowerView, - refreshProjectPowerView, -} from '../repositories/projectPowerViewRepository'; -import { User } from '../entities/user'; -import { Project } from '../entities/project'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from '../repositories/powerBoostingRepository'; -import { setPowerRound } from '../repositories/powerRoundRepository'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { AppDataSource } from '../orm'; -import { addOrUpdatePowerSnapshotBalances } from '../repositories/powerBalanceSnapshotRepository'; -import { findPowerSnapshots } from '../repositories/powerSnapshotRepository'; - -describe('userProjectPowers test cases', projectPowersTestCases); - -function projectPowersTestCases() { - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBoosting.clear(); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - it('should return one where there is no project ranked', async () => { - await refreshProjectPowerView(); - const result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 100 }, - }); - - assert.isOk(result); - assert.equal(result.data.data.powerAmountRank, 1); - }); - - it('should return correct rank', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project3.id * 10; - - await Promise.all( - [ - [user1, project1, 10], // 1000 - [user1, project2, 20], // 2000 - [user1, project3, 30], // 3000 - ].map(item => { - const [user, project, percentage] = item as [User, Project, number]; - return insertSinglePowerBoosting({ - user, - project, - percentage, - }); - }), - ); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }); - - await setPowerRound(roundNumber - 1); - await refreshProjectFuturePowerView(); - - // 1. Higher than all -> must return 1 - let result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 4000 }, - }); - - assert.isOk(result); - assert.equal(result.data.data.powerAmountRank, 1); - - // 2. Between some, must return correct rank - result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 1500 }, // 3000, 2000, [1500], 1000 - }); - - assert.isOk(result); - assert.equal(result.data.data.powerAmountRank, 3); - - // 2. Lower than all - result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 900 }, // 3000, 2000, 1000, [900] - }); - - assert.isOk(result); - assert.equal(result.data.data.powerAmountRank, 4); - }); - - it('should be round independent', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project3.id * 10; - - await Promise.all( - [ - [user1, project1, 10], // 1000 - [user1, project2, 20], // 2000 - [user1, project3, 30], // 3000 - ].map(item => { - const [user, project, percentage] = item as [User, Project, number]; - return insertSinglePowerBoosting({ - user, - project, - percentage, - }); - }), - ); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber - 1; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }); - - await setPowerRound(roundNumber - 1); - await refreshProjectFuturePowerView(); - - let result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 1500 }, // 3000, 2000, [1500], 1000 - }); - - assert.isOk(result); - assert.equal(result.data.data.powerAmountRank, 3); - - // Move to the next round - await setPowerRound(roundNumber); - await refreshProjectFuturePowerView(); - - result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 1500 }, // 3000, 2000, [1500], 1000 - }); - - assert.isOk(result); - assert.equal(result.data.data.powerAmountRank, 3); - }); - - it('should update by new snapshot data', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project3.id * 10; - - const powerBoostings = await Promise.all( - [ - [user1, project1, 10], - [user1, project2, 20], - [user1, project3, 30], - [user2, project1, 30], - [user2, project2, 20], - [user2, project3, 10], - ].map(item => { - const [user, project, percentage] = item as [User, Project, number]; - return insertSinglePowerBoosting({ - user, - project, - percentage, - }); - }), - ); - - await takePowerBoostingSnapshot(); - let [powerSnapshots] = await findPowerSnapshots(); - let snapshot = powerSnapshots[0]; - - snapshot.roundNumber = roundNumber - 1; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }, - { - userId: user2.id, - powerSnapshotId: snapshot.id, - balance: 50000, - }, - ]); - - await setPowerRound(roundNumber - 1); - await refreshProjectFuturePowerView(); - - // Project1: 1_000 + 15_000 = 16_000 - // Project2: 2_000 + 10_000 = 10_000 - // Project3: 3_000 + 5_000 = 8_000 - - let result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 9000 }, // 16000, 10000, [9000], 8000 - }); - - assert.isOk(result); - assert.equal(result.data.data.powerAmountRank, 3); - - // Next snapshot - - powerBoostings[0].percentage = 40; // user 1 project 1 - powerBoostings[1].percentage = 30; // user 1 project 2 - powerBoostings[2].percentage = 20; // user 1 project 3 - powerBoostings[3].percentage = 10; // user 2 project 1 - powerBoostings[4].percentage = 20; // user 2 project 2 - powerBoostings[5].percentage = 30; // user 2 project 2 - - await PowerBoosting.save(powerBoostings); - - await takePowerBoostingSnapshot(); - [powerSnapshots] = await findPowerSnapshots(); - snapshot = powerSnapshots[1]; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 20000, - }, - { - userId: user2.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }, - ]); - - // Project1: 8000 + 1000 = 9_000 - // Project2: 6000 + 2000 = 8_000 - // Project2: 4000 + 3000 = 7_000 - await refreshProjectFuturePowerView(); - - result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 9001 }, - }); - - assert.isOk(result); - assert.equal(result.data.data.powerAmountRank, 1); - - result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 8999 }, - }); - - assert.isOk(result); - assert.equal(result.data.data.powerAmountRank, 2); - }); - - it('should return correct rank, when projectId is passed', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - - const roundNumber = project3.id * 10; - - const boostings = await Promise.all( - [ - [user1, project1, 10], // 1000 - [user1, project2, 20], // 2000 - [user1, project3, 30], // 3000 - ].map(item => { - const [user, project, percentage] = item as [User, Project, number]; - return insertSinglePowerBoosting({ - user, - project, - percentage, - }); - }), - ); - - await takePowerBoostingSnapshot(); - let [powerSnapshots] = await findPowerSnapshots(); - let snapshot = powerSnapshots[0]; - - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }); - - await setPowerRound(roundNumber - 1); - await refreshProjectFuturePowerView(); - - // project3 power is 3000 - // Querying 2999 (lower than rank 1 power) power with passing project3 id - let result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 2999, projectId: project3.id }, - }); - assert.equal(result.data.data.powerAmountRank, 1); - - // Querying 2999 power without passing project3 id - result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 2999 }, - }); - assert.equal(result.data.data.powerAmountRank, 2); - - // Querying 2000 (rank 2 power) power with passing project3 id - result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 2000, projectId: project3.id }, - }); - assert.equal(result.data.data.powerAmountRank, 1); - - // Querying 2000 (rank 2 power) power with passing project2 id (rank 2) - result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 2000, projectId: project2.id }, // 3000, 2000, [1500], 1000 - }); - assert.equal(result.data.data.powerAmountRank, 2); - - // Two projects with same rank - boostings[0].percentage = 20; // Project 1 -> 20% = 2000 - boostings[1].percentage = 20; // Project 2 -> 20% = 2000 - boostings[2].percentage = 40; // Project 3 -> 40% = 4000 - await PowerBoosting.save(boostings); - - await takePowerBoostingSnapshot(); - - [powerSnapshots] = await findPowerSnapshots(); - snapshot = powerSnapshots[1]; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }); - - await setPowerRound(roundNumber - 1); - await refreshProjectFuturePowerView(); - - result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 2000, projectId: project2.id }, - }); - - assert.equal(result.data.data.powerAmountRank, 2); - - result = await axios.post(graphqlUrl, { - query: getPowerAmountRankQuery, - variables: { powerAmount: 1999, projectId: project2.id }, - }); - - assert.equal(result.data.data.powerAmountRank, 3); - }); -} diff --git a/src/resolvers/projectPowerResolver.ts b/src/resolvers/projectPowerResolver.ts deleted file mode 100644 index ed1724e6a..000000000 --- a/src/resolvers/projectPowerResolver.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Arg, Query, Resolver, Int } from 'type-graphql'; -import { getPowerAmountRank } from '../repositories/projectPowerViewRepository'; -import { ProjectPowerView } from '../views/projectPowerView'; - -@Resolver(_of => ProjectPowerView) -export class ProjectPowerResolver { - @Query(_returns => Int) - async powerAmountRank( - @Arg('powerAmount', { nullable: false }) powerAmount: number, - @Arg('projectId', _type => Int, { nullable: true }) projectId: number, - ): Promise { - return await getPowerAmountRank(powerAmount, projectId); - } -} diff --git a/src/resolvers/projectResolver.allProject.test.ts b/src/resolvers/projectResolver.allProject.test.ts deleted file mode 100644 index b20bd5e97..000000000 --- a/src/resolvers/projectResolver.allProject.test.ts +++ /dev/null @@ -1,2145 +0,0 @@ -import { assert } from 'chai'; -import 'mocha'; -import axios from 'axios'; -import moment from 'moment'; -import { - createDonationData, - createProjectData, - generateRandomEtheriumAddress, - generateRandomSolanaAddress, - graphqlUrl, - REACTION_SEED_DATA, - saveDonationDirectlyToDb, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, - SEED_DATA, -} from '../../test/testUtils'; -import { fetchMultiFilterAllProjectsQuery } from '../../test/graphqlQueries'; -import { Project, ReviewStatus, SortingField } from '../entities/project'; -import { User } from '../entities/user'; -import { NETWORK_IDS } from '../provider'; -import { findProjectRecipientAddressByNetworkId } from '../repositories/projectAddressRepository'; -import { setPowerRound } from '../repositories/powerRoundRepository'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from '../repositories/powerBoostingRepository'; -import { refreshProjectPowerView } from '../repositories/projectPowerViewRepository'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { ProjectAddress } from '../entities/projectAddress'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { AppDataSource } from '../orm'; -// We are using cache so redis needs to be cleared for tests with same filters -import { redis } from '../redis'; -import { Campaign, CampaignType } from '../entities/campaign'; -import { generateRandomString, getHtmlTextSummary } from '../utils/utils'; -import { InstantPowerBalance } from '../entities/instantPowerBalance'; -import { saveOrUpdateInstantPowerBalances } from '../repositories/instantBoostingRepository'; -import { updateInstantBoosting } from '../services/instantBoostingServices'; -import { QfRound } from '../entities/qfRound'; -import { calculateEstimatedMatchingWithParams } from '../utils/qfUtils'; -import { refreshProjectEstimatedMatchingView } from '../services/projectViewsService'; -import { addOrUpdatePowerSnapshotBalances } from '../repositories/powerBalanceSnapshotRepository'; -import { findPowerSnapshots } from '../repositories/powerSnapshotRepository'; -import { ChainType } from '../types/network'; -import { ORGANIZATION_LABELS } from '../entities/organization'; - -// search and filters -describe('all projects test cases --->', allProjectsTestCases); - -function allProjectsTestCases() { - beforeEach(async () => { - // Make all existing qfRounds inactive - await QfRound.update({}, { isActive: false }); - }); - it('should return projects search by title', async () => { - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - searchTerm: SEED_DATA.FIRST_PROJECT.title, - }, - }); - - const projects = result.data.data.allProjects.projects; - - assert.isTrue(projects.length > 0); - assert.equal(projects[0]?.adminUserId, SEED_DATA.FIRST_PROJECT.adminUserId); - assert.isNotEmpty(projects[0].addresses); - projects.forEach(project => { - assert.isNotOk(project.adminUser.email); - assert.isOk(project.adminUser.firstName); - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.categories[0].mainCategory.title); - assert.equal( - project.descriptionSummary, - getHtmlTextSummary(project.description), - ); - }); - }); - - it('should return projects with correct reaction', async () => { - const limit = 1; - const USER_DATA = SEED_DATA.FIRST_USER; - - // Project has not been liked - let result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - limit, - searchTerm: SEED_DATA.SECOND_PROJECT.title, - connectedWalletUserId: USER_DATA.id, - }, - }); - - let projects = result.data.data.allProjects.projects; - assert.equal(projects.length, limit); - assert.isNull(projects[0]?.reaction); - - // Project has been liked, but connectedWalletUserIs is not filled - result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - limit, - searchTerm: SEED_DATA.FIRST_PROJECT.title, - }, - }); - - projects = result.data.data.allProjects.projects; - assert.equal(projects.length, limit); - assert.isNull(projects[0]?.reaction); - - // Project has been liked - result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - searchTerm: SEED_DATA.FIRST_PROJECT.title, - connectedWalletUserId: USER_DATA.id, - }, - }); - - projects = result.data.data.allProjects.projects; - // Find the project with the exact title - const selectedProject = projects.find( - ({ title }) => title === SEED_DATA.FIRST_PROJECT.title, - ); - assert.isAtLeast(projects.length, limit); - assert.equal( - selectedProject?.reaction?.id, - REACTION_SEED_DATA.FIRST_LIKED_PROJECT_REACTION.id, - ); - projects.forEach(project => { - assert.isNotOk(project.adminUser.email); - assert.isOk(project.adminUser.firstName); - assert.isOk(project.adminUser.walletAddress); - }); - }); - - it('should return projects, sort by creationDate, DESC', async () => { - const firstProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const secondProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - sortingBy: SortingField.Newest, - }, - }); - const projects = result.data.data.allProjects.projects; - - const secondProjectIndex = projects.findIndex( - project => Number(project.id) === secondProject.id, - ); - const firstProjectIndex = projects.findIndex( - project => Number(project.id) === firstProject.id, - ); - - assert( - secondProjectIndex !== -1, - 'Second project not found in the projects list', - ); - assert( - firstProjectIndex !== -1, - 'First project not found in the projects list', - ); - - assert( - secondProjectIndex < firstProjectIndex, - "Second project's index is not lower than the first project's index", - ); - }); - - it('should return projects, sort by updatedAt, DESC', async () => { - const firstProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - firstProject.title = String(new Date().getTime()); - firstProject.updatedAt = moment().add(2, 'days').toDate(); - await firstProject.save(); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - sortingBy: SortingField.RecentlyUpdated, - }, - }); - // First project should move to first position - assert.equal( - Number(result.data.data.allProjects.projects[0].id), - firstProject.id, - ); - }); - it('should return projects, sort by creationDate, ASC', async () => { - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - sortingBy: SortingField.Oldest, - }, - }); - const projectsCount = result.data.data.allProjects.projects.length; - const firstProjectIsOlder = - new Date(result.data.data.allProjects.projects[0].creationDate) < - new Date( - result.data.data.allProjects.projects[projectsCount - 1].creationDate, - ); - assert.isTrue(firstProjectIsOlder); - }); - it('should return projects, filter by verified, true', async () => { - // There is two verified projects so I just need to create a project with verified: false and listed:true - await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - verified: false, - qualityScore: 0, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['Verified'], - }, - }); - assert.isNotEmpty(result.data.data.allProjects.projects); - result.data.data.allProjects.projects.forEach(project => - assert.isTrue(project.verified), - ); - }); - it('should return projects, filter by acceptGiv, true', async () => { - await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - qualityScore: 0, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptGiv'], - }, - }); - assert.isNotEmpty(result.data.data.allProjects.projects); - result.data.data.allProjects.projects.forEach(project => - // currently givingBlocks projects doesnt accept GIV - assert.notExists(project.givingBlocksId), - ); - }); - it('should return projects, filter by boosted by givPower, true', async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBoosting.clear(); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - }); - - const roundNumber = project.id * 10; - await insertSinglePowerBoosting({ - user, - project, - percentage: 100, - }); - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 200, - }); - - await setPowerRound(roundNumber); - await refreshProjectPowerView(); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['BoostedWithGivPower'], - limit: 50, - }, - }); - assert.isNotEmpty(result.data.data.allProjects.projects); - result.data.data.allProjects.projects.forEach(projectQueried => - assert.isOk(projectQueried?.projectPower?.totalPower > 0), - ); - }); - it('should return projects, filter from the ENDAOMENT', async () => { - await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - qualityScore: 0, - organizationLabel: ORGANIZATION_LABELS.ENDAOMENT, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['Endaoment'], - }, - }); - assert.isNotEmpty(result.data.data.allProjects.projects); - result.data.data.allProjects.projects.forEach(project => - assert.exists(project.organization.label, ORGANIZATION_LABELS.ENDAOMENT), - ); - }); - it('should return projects, sort by reactions, DESC', async () => { - await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - totalReactions: 100, - qualityScore: 0, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - sortingBy: SortingField.MostLiked, - }, - }); - assert.isTrue( - result.data.data.allProjects.projects[0].totalReactions >= 100, - ); - }); - it('should return projects, sort by donations, DESC', async () => { - await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - totalDonations: 100, - qualityScore: 0, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - sortingBy: SortingField.MostFunded, - }, - }); - assert.isTrue( - result.data.data.allProjects.projects[0].totalDonations >= 100, - ); - }); - it('should return projects, sort by qualityScore, DESC', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - totalDonations: 100, - qualityScore: 10000, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - sortingBy: SortingField.QualityScore, - }, - }); - assert.isTrue( - Number(result.data.data.allProjects.projects[0].id) === project.id, - ); - - // default sort - const result2 = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - }); - assert.isTrue( - Number(result2.data.data.allProjects.projects[0].id) === project.id, - ); - }); - - // it('should return projects, sort by project raised funds in the active QF round DESC', async () => { - // const donor = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - // const project1 = await saveProjectDirectlyToDb({ - // ...createProjectData(), - // title: String(new Date().getTime()), - // slug: String(new Date().getTime()), - // }); - // const project2 = await saveProjectDirectlyToDb({ - // ...createProjectData(), - // title: String(new Date().getTime()), - // slug: String(new Date().getTime()), - // }); - - // const qfRound = await QfRound.create({ - // isActive: true, - // name: 'test filter by qfRoundId', - // minimumPassportScore: 10, - // allocatedFund: 100, - // beginDate: new Date(), - // endDate: moment().add(1, 'day').toDate(), - // }).save(); - // project1.qfRounds = [qfRound]; - // await project1.save(); - // project2.qfRounds = [qfRound]; - // await project2.save(); - - // const donation1 = await saveDonationDirectlyToDb( - // { - // ...createDonationData(), - // status: 'verified', - // qfRoundId: qfRound.id, - // valueUsd: 2, - // }, - // donor.id, - // project1.id, - // ); - - // const donation2 = await saveDonationDirectlyToDb( - // { - // ...createDonationData(), - // status: 'verified', - // qfRoundId: qfRound.id, - // valueUsd: 20, - // }, - // donor.id, - // project2.id, - // ); - - // await refreshProjectEstimatedMatchingView(); - - // const result = await axios.post(graphqlUrl, { - // query: fetchMultiFilterAllProjectsQuery, - // variables: { - // sortingBy: SortingField.ActiveQfRoundRaisedFunds, - // limit: 10, - // }, - // }); - - // assert.equal(result.data.data.allProjects.projects.length, 2); - // assert.equal(result.data.data.allProjects.projects[0].id, project2.id); - // result.data.data.allProjects.projects.forEach(project => { - // assert.equal(project.qfRounds[0].id, qfRound.id); - // }); - // qfRound.isActive = false; - // await qfRound.save(); - // }); - - it('should return projects, sort by project instant power DESC', async () => { - await PowerBoosting.clear(); - await InstantPowerBalance.clear(); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - const project4 = await saveProjectDirectlyToDb({ - ...createProjectData(), - verified: false, - }); // Not boosted -Not verified project - await saveProjectDirectlyToDb(createProjectData()); // Not boosted project - - await Promise.all( - [ - [user1, project1, 10], - [user1, project2, 20], - [user1, project3, 30], - [user1, project4, 40], - [user2, project1, 20], - [user2, project2, 40], - [user2, project3, 60], - ].map(item => { - const [user, project, percentage] = item as [User, Project, number]; - return insertSinglePowerBoosting({ - user, - project, - percentage, - }); - }), - ); - - await saveOrUpdateInstantPowerBalances([ - { - userId: user1.id, - balance: 10000, - balanceAggregatorUpdatedAt: new Date(1_000_000), - }, - { - userId: user2.id, - balance: 1000, - balanceAggregatorUpdatedAt: new Date(1_000_000), - }, - ]); - - await updateInstantBoosting(); - - let result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - sortingBy: SortingField.InstantBoosting, - limit: 50, - }, - }); - - let projects = result.data.data.allProjects.projects; - - assert.equal(projects[0].id, project3.id); - assert.equal(projects[1].id, project2.id); - assert.equal(projects[2].id, project1.id); - - assert.equal(projects[0].projectInstantPower.powerRank, 1); - assert.equal(projects[1].projectInstantPower.powerRank, 2); - assert.equal(projects[2].projectInstantPower.powerRank, 3); - assert.equal(projects[3].projectInstantPower.powerRank, 4); - - result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - sortingBy: SortingField.InstantBoosting, - }, - }); - - projects = result.data.data.allProjects.projects; - const totalCount = projects.length; - for (let i = 1; i < totalCount - 1; i++) { - assert.isTrue( - projects[i].projectInstantPower.totalPower <= - projects[i - 1].projectInstantPower.totalPower, - ); - assert.isTrue( - projects[i].projectInstantPower.powerRank >= - projects[i - 1].projectInstantPower.powerRank, - ); - - if (projects[i].verified === true) { - // verified project come first - assert.isTrue(projects[i - 1].verified); - } - } - }); - - it('should return projects, sort by project power DESC', async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBoosting.clear(); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - await saveProjectDirectlyToDb({ - ...createProjectData(), - verified: false, - }); // Not boosted -Not verified project - await saveProjectDirectlyToDb(createProjectData()); // Not boosted project - - const roundNumber = project3.id * 10; - - await Promise.all( - [ - [user1, project1, 10], - [user1, project2, 20], - [user1, project3, 30], - [user2, project1, 20], - [user2, project2, 40], - [user2, project3, 60], - ].map(item => { - const [user, project, percentage] = item as [User, Project, number]; - return insertSinglePowerBoosting({ - user, - project, - percentage, - }); - }), - ); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }, - { - userId: user2.id, - powerSnapshotId: snapshot.id, - balance: 20000, - }, - ]); - - await setPowerRound(roundNumber); - await refreshProjectPowerView(); - - let result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - sortingBy: SortingField.GIVPower, - limit: 50, - }, - }); - - let projects = result.data.data.allProjects.projects; - - assert.equal(projects[0].id, project3.id); - assert.equal(projects[1].id, project2.id); - assert.equal(projects[2].id, project1.id); - - assert.equal(projects[0].projectPower.powerRank, 1); - assert.equal(projects[1].projectPower.powerRank, 2); - assert.equal(projects[2].projectPower.powerRank, 3); - assert.equal(projects[3].projectPower.powerRank, 4); - - result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - sortingBy: SortingField.GIVPower, - }, - }); - - projects = result.data.data.allProjects.projects; - const totalCount = projects.length; - for (let i = 1; i < totalCount - 1; i++) { - assert.isTrue( - projects[i].projectPower.totalPower <= - projects[i - 1].projectPower.totalPower, - ); - assert.isTrue( - projects[i].projectPower.powerRank >= - projects[i - 1].projectPower.powerRank, - ); - - if (projects[i].verified === true) { - // verified project come first - assert.isTrue(projects[i - 1].verified); - } - } - }); - - it('should return projects, filtered by sub category', async () => { - await saveProjectDirectlyToDb({ - ...createProjectData(), - categories: ['food5'], - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - category: 'food5', - }, - }); - assert.isNotEmpty(result.data.data.allProjects.projects); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.categories.find(category => category.name === 'food5'), - ); - }); - }); - it('should return projects, filtered by main category', async () => { - await saveProjectDirectlyToDb({ - ...createProjectData(), - categories: ['drink2'], - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - mainCategory: 'drink', - }, - }); - assert.isNotEmpty(result.data.data.allProjects.projects); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.categories.find( - category => category.mainCategory.title === 'drink', - ), - ); - }); - }); - it('should return projects, filtered by main category and sub category at the same time', async () => { - await saveProjectDirectlyToDb({ - ...createProjectData(), - categories: ['drink2'], - }); - await saveProjectDirectlyToDb({ - ...createProjectData(), - categories: ['drink3'], - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - mainCategory: 'drink', - category: 'drink3', - }, - }); - assert.isNotEmpty(result.data.data.allProjects.projects); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.categories.find( - category => category.mainCategory.title === 'drink', - ), - ); - - // Should not return projects with drink2 category - assert.isOk( - project.categories.find(category => category.name === 'drink3'), - ); - }); - }); - - it('should return projects, filter by accept donation on gnosis, not return when it doesnt have gnosis address', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const gnosisAddress = (await findProjectRecipientAddressByNetworkId({ - projectId: savedProject.id, - networkId: NETWORK_IDS.XDAI, - })) as ProjectAddress; - gnosisAddress.isRecipient = false; - await gnosisAddress.save(); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnGnosis'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - address.networkId === NETWORK_IDS.XDAI && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isNotOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on celo', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.CELO, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnCelo'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.CELO || - address.networkId === NETWORK_IDS.CELO_ALFAJORES), - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - it('should return projects, filter by accept donation on celo', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.CELO, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnCelo'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.CELO || - address.networkId === NETWORK_IDS.CELO_ALFAJORES) && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on celo, not return when it doesnt have celo address', async () => { - const celoProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.CELO, - }); - const polygonProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.POLYGON, - }); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnCelo'], - sortingBy: SortingField.Newest, - }, - }); - - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.CELO || - address.networkId === NETWORK_IDS.CELO_ALFAJORES) && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isNotOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(polygonProject.id), - ), - ); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(celoProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on arbitrum', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.ARBITRUM_MAINNET, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnArbitrum'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.ARBITRUM_MAINNET || - address.networkId === NETWORK_IDS.ARBITRUM_SEPOLIA), - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - it('should return projects, filter by accept donation on arbitrum', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.ARBITRUM_MAINNET, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnArbitrum'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.ARBITRUM_MAINNET || - address.networkId === NETWORK_IDS.ARBITRUM_SEPOLIA) && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - it('should return projects, filter by accept donation on arbitrum, not return when it doesnt have arbitrum address', async () => { - const arbitrumProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.ARBITRUM_MAINNET, - }); - const polygonProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.POLYGON, - }); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnArbitrum'], - sortingBy: SortingField.Newest, - }, - }); - - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.ARBITRUM_MAINNET || - address.networkId === NETWORK_IDS.ARBITRUM_SEPOLIA) && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isNotOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(polygonProject.id), - ), - ); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(arbitrumProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on base', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.BASE_MAINNET, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnBase'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.BASE_MAINNET || - address.networkId === NETWORK_IDS.BASE_SEPOLIA), - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - it('should return projects, filter by accept donation on base', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.BASE_MAINNET, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnBase'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.BASE_MAINNET || - address.networkId === NETWORK_IDS.BASE_SEPOLIA) && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - it('should return projects, filter by accept donation on base, not return when it doesnt have base address', async () => { - const baseProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.BASE_MAINNET, - }); - const polygonProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.POLYGON, - }); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnBase'], - sortingBy: SortingField.Newest, - }, - }); - - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.BASE_MAINNET || - address.networkId === NETWORK_IDS.BASE_SEPOLIA) && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isNotOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(polygonProject.id), - ), - ); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(baseProject.id), - ), - ); - }); - - /////// Beginning of ZKEVM filter test - it('should return projects, filter by accept donation on zkevm', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.ZKEVM_MAINNET, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnZKEVM'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.ZKEVM_MAINNET || - address.networkId === NETWORK_IDS.ZKEVM_CARDONA), - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - it('should return projects, filter by accept donation on zkevm', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.ZKEVM_MAINNET, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnZKEVM'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.ZKEVM_MAINNET || - address.networkId === NETWORK_IDS.ZKEVM_CARDONA) && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - it('should return projects, filter by accept donation on base, not return when it doesnt have zkevm address', async () => { - const zkevmProject1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.ZKEVM_MAINNET, - }); - const zkevmCardanoProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.ZKEVM_CARDONA, - }); - const polygonProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.POLYGON, - }); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnZKEVM'], - sortingBy: SortingField.Newest, - }, - }); - - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.ZKEVM_MAINNET || - address.networkId === NETWORK_IDS.ZKEVM_CARDONA) && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isNotOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(polygonProject.id), - ), - ); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(zkevmProject1.id), - ), - ); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(zkevmCardanoProject.id), - ), - ); - }); - - /////// End of ZKEVM filter test - - it('should return projects, filter by accept donation on mainnet', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.MAIN_NET, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnMainnet'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.MAIN_NET || - address.networkId === NETWORK_IDS.GOERLI) && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on mainnet, not return when it doesnt have mainnet address', async () => { - const polygonProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.POLYGON, - }); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnMainnet'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - address.networkId === NETWORK_IDS.MAIN_NET && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isNotOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(polygonProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on polygon', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const polygonAddress = (await findProjectRecipientAddressByNetworkId({ - projectId: savedProject.id, - networkId: NETWORK_IDS.POLYGON, - })) as ProjectAddress; - polygonAddress.isRecipient = true; - await polygonAddress.save(); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnPolygon'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - address.networkId === NETWORK_IDS.POLYGON && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on polygon, not return when it doesnt have polygon address', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.OPTIMISTIC, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnPolygon'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - address.networkId === NETWORK_IDS.POLYGON && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isNotOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on GOERLI', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.GOERLI, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnMainnet'], - sortingBy: SortingField.Newest, - limit: 50, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - (address.isRecipient === true && - address.networkId === NETWORK_IDS.MAIN_NET) || - address.networkId === NETWORK_IDS.GOERLI, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on ALFAJORES', async () => { - const alfajoresProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.CELO_ALFAJORES, - }); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnCelo'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - (address.isRecipient === true && - // We return both Celo and Alfajores when sending AcceptFundOnCelo filter - address.networkId === NETWORK_IDS.CELO_ALFAJORES) || - address.networkId === NETWORK_IDS.CELO, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(alfajoresProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on Arbitrum Sepolia', async () => { - const arbSepoliaProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.ARBITRUM_SEPOLIA, - }); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnArbitrum'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - (address.isRecipient === true && - // We return both Arbitrum Mainnet and Arbitrum Sepolia when sending AcceptFundOnArbitrum filter - address.networkId === NETWORK_IDS.ARBITRUM_SEPOLIA) || - address.networkId === NETWORK_IDS.ARBITRUM_MAINNET, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(arbSepoliaProject.id), - ), - ); - }); - - it('should return projects, filter by accept donation on optimism', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.OPTIMISTIC, - }); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnOptimism'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - address.networkId === NETWORK_IDS.OPTIMISTIC && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - it('should return projects, filter by accept donation on optimism, not return when it doesnt have optimism address', async () => { - const gnosisProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.XDAI, - }); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnOptimism'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - (address.networkId === NETWORK_IDS.OPTIMISTIC || - address.networkId === NETWORK_IDS.OPTIMISM_SEPOLIA) && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isNotOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(gnosisProject.id), - ), - ); - }); - it('should return projects, filter by accept donation on gnosis, return all addresses', async () => { - await redis.flushall(); // clear cache from other tests - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnGnosis'], - sortingBy: SortingField.Newest, - limit: 50, - }, - }); - result.data.data.allProjects.projects.forEach(item => { - assert.isOk( - item.addresses.find( - address => - address.isRecipient === true && - address.networkId === NETWORK_IDS.XDAI && - address.chainType === ChainType.EVM, - ), - ); - }); - const project = result.data.data.allProjects.projects.find( - item => Number(item.id) === Number(savedProject.id), - ); - - assert.isOk(project); - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - address.networkId === NETWORK_IDS.XDAI && - address.chainType === ChainType.EVM, - ), - ); - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - address.networkId === NETWORK_IDS.MAIN_NET && - address.chainType === ChainType.EVM, - ), - ); - }); - it('should return projects, filter by accept donation on gnosis, should not return if it has no address', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - await ProjectAddress.query(` - DELETE - from project_address - WHERE "projectId" = ${savedProject.id} - `); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnGnosis'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - address.networkId === NETWORK_IDS.XDAI && - address.chainType === ChainType.EVM, - ), - ); - }); - assert.isNotOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - it('should return projects, filter by accept donation on Solana', async () => { - const savedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - await ProjectAddress.delete({ projectId: savedProject.id }); - const solanaAddress = ProjectAddress.create({ - project: savedProject, - title: 'first address', - address: generateRandomSolanaAddress(), - chainType: ChainType.SOLANA, - networkId: 0, - isRecipient: true, - }); - await solanaAddress.save(); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnSolana'], - sortingBy: SortingField.Newest, - }, - }); - result.data.data.allProjects.projects.forEach(project => { - assert.isOk( - project.addresses.find( - address => - address.isRecipient === true && - address.chainType === ChainType.SOLANA, - ), - ); - }); - assert.isOk( - result.data.data.allProjects.projects.find( - project => Number(project.id) === Number(savedProject.id), - ), - ); - }); - it('should return projects, filter by accept fund on two Ethereum networks', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - const mainnetAddress = ProjectAddress.create({ - project, - title: 'first address', - address: generateRandomEtheriumAddress(), - networkId: 1, - isRecipient: true, - }); - await mainnetAddress.save(); - - const solanaAddress = ProjectAddress.create({ - project, - title: 'secnod address', - address: generateRandomSolanaAddress(), - chainType: ChainType.SOLANA, - networkId: 0, - isRecipient: true, - }); - await solanaAddress.save(); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnMainnet', 'AcceptFundOnSolana'], - sortingBy: SortingField.Newest, - }, - }); - const { projects } = result.data.data.allProjects; - - const projectIds = projects.map(_project => _project.id); - assert.include(projectIds, String(project.id)); - }); - it('should return projects, when only accpets donation on Solana or an expected Ethereum network', async () => { - const projectWithMainnet = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const projectWithSolana = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - const mainnetAddress = ProjectAddress.create({ - project: projectWithMainnet, - title: 'first address', - address: generateRandomEtheriumAddress(), - networkId: 1, - isRecipient: true, - }); - await mainnetAddress.save(); - - const solanaAddress = ProjectAddress.create({ - project: projectWithSolana, - title: 'secnod address', - address: generateRandomSolanaAddress(), - chainType: ChainType.SOLANA, - networkId: 0, - isRecipient: true, - }); - await solanaAddress.save(); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnMainnet', 'AcceptFundOnSolana'], - sortingBy: SortingField.Newest, - }, - }); - const { projects } = result.data.data.allProjects; - const projectIds = projects.map(project => project.id); - assert.include(projectIds, String(projectWithMainnet.id)); - assert.include(projectIds, String(projectWithSolana.id)); - }); - it('should not return a project when it does not accept donation on Solana', async () => { - // Delete all project addresses - await ProjectAddress.delete({ chainType: ChainType.SOLANA }); - - await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['AcceptFundOnSolana'], - sortingBy: SortingField.Newest, - }, - }); - const { projects } = result.data.data.allProjects; - assert.lengthOf(projects, 0); - }); - it('should return projects, filter by campaignSlug and limit, skip', async () => { - const project1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const project2 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const project3 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - const campaign = await Campaign.create({ - isActive: true, - type: CampaignType.ManuallySelected, - slug: generateRandomString(), - title: 'title1', - description: 'description1', - photo: 'https://google.com', - relatedProjectsSlugs: [ - project1.slug as string, - project2.slug as string, - project3.slug as string, - ], - order: 1, - }).save(); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - limit: 1, - skip: 1, - campaignSlug: campaign.slug, - }, - }); - - assert.equal(result.data.data.allProjects.projects.length, 1); - assert.equal(result.data.data.allProjects.campaign.title, campaign.title); - assert.isOk( - [project1.slug, project2.slug, project3.slug].includes( - result.data.data.allProjects.projects[0].slug, - ), - ); - - await campaign.remove(); - }); - it('should return projects, filter by qfRoundId', async () => { - const project1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const project2 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const project3 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - const qfRound = await QfRound.create({ - isActive: true, - name: 'test filter by qfRoundId', - slug: new Date().getTime().toString(), - minimumPassportScore: 10, - allocatedFund: 100, - beginDate: new Date(), - endDate: new Date(), - }).save(); - project1.qfRounds = [qfRound]; - await project1.save(); - project2.qfRounds = [qfRound]; - await project2.save(); - project3.qfRounds = [qfRound]; - await project3.save(); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - qfRoundId: qfRound.id, - }, - }); - - assert.equal(result.data.data.allProjects.projects.length, 3); - result.data.data.allProjects.projects.forEach(project => { - assert.equal(project.qfRounds[0].id, qfRound.id); - }); - qfRound.isActive = false; - await qfRound.save(); - }); - it('should return projects, filter by qfRoundSlug', async () => { - const project1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const project2 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const project3 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - const qfRound = await QfRound.create({ - isActive: true, - name: 'test filter by qfRoundId', - slug: new Date().getTime().toString(), - minimumPassportScore: 10, - allocatedFund: 100, - beginDate: new Date(), - endDate: new Date(), - }).save(); - project1.qfRounds = [qfRound]; - await project1.save(); - project2.qfRounds = [qfRound]; - await project2.save(); - project3.qfRounds = [qfRound]; - await project3.save(); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - qfRoundSlug: qfRound.slug, - }, - }); - - assert.equal(result.data.data.allProjects.projects.length, 3); - result.data.data.allProjects.projects.forEach(project => { - assert.equal(project.qfRounds[0].id, qfRound.id); - }); - qfRound.isActive = false; - await qfRound.save(); - }); - it('should just return verified projects, filter by qfRoundId and verified', async () => { - const project1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const project2 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const project3 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - verified: false, - }); - - const qfRound = await QfRound.create({ - isActive: true, - name: 'test filter by qfRoundId', - slug: new Date().getTime().toString(), - minimumPassportScore: 10, - allocatedFund: 100, - beginDate: new Date(), - endDate: new Date(), - }).save(); - project1.qfRounds = [qfRound]; - await project1.save(); - project2.qfRounds = [qfRound]; - await project2.save(); - project3.qfRounds = [qfRound]; - await project3.save(); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - qfRoundId: qfRound.id, - filters: ['Verified'], - }, - }); - - assert.equal(result.data.data.allProjects.projects.length, 2); - result.data.data.allProjects.projects.forEach(project => { - assert.equal(project.qfRounds[0].id, qfRound.id); - }); - qfRound.isActive = false; - await qfRound.save(); - }); - it('should return projects, filter by qfRoundId, calculate estimated matching', async () => { - const project1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const project2 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - const qfRound = await QfRound.create({ - isActive: true, - name: new Date().toString(), - slug: new Date().getTime().toString(), - minimumPassportScore: 8, - allocatedFund: 1000, - beginDate: new Date(), - endDate: moment().add(10, 'days').toDate(), - }).save(); - project1.qfRounds = [qfRound]; - await project1.save(); - project2.qfRounds = [qfRound]; - await project2.save(); - - const donor1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - donor1.passportScore = 13; - await donor1.save(); - - const donor2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - donor2.passportScore = 13; - await donor2.save(); - - // We should have result similar to https://wtfisqf.com/?grant=2,2&grant=4&grant=&grant=&match=1000 - await saveDonationDirectlyToDb( - { - ...createDonationData(), - status: 'verified', - qfRoundId: qfRound.id, - valueUsd: 2, - }, - donor1.id, - project1.id, - ); - - await saveDonationDirectlyToDb( - { - ...createDonationData(), - status: 'verified', - qfRoundId: qfRound.id, - valueUsd: 2, - }, - donor2.id, - project1.id, - ); - - await saveDonationDirectlyToDb( - { - ...createDonationData(), - status: 'verified', - qfRoundId: qfRound.id, - valueUsd: 4, - }, - donor1.id, - project2.id, - ); - - await refreshProjectEstimatedMatchingView(); - - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - filters: ['ActiveQfRound'], - sortingBy: SortingField.EstimatedMatching, - }, - }); - - assert.equal(result.data.data.allProjects.projects.length, 2); - const firstProject = result.data.data.allProjects.projects.find( - p => Number(p.id) === project1.id, - ); - const secondProject = result.data.data.allProjects.projects.find( - p => Number(p.id) === project2.id, - ); - - const project1EstimatedMatching = - await calculateEstimatedMatchingWithParams({ - matchingPool: firstProject.estimatedMatching.matchingPool, - projectDonationsSqrtRootSum: - firstProject.estimatedMatching.projectDonationsSqrtRootSum, - allProjectsSum: firstProject.estimatedMatching.allProjectsSum, - }); - - const project2EstimatedMatching = - await calculateEstimatedMatchingWithParams({ - matchingPool: secondProject.estimatedMatching.matchingPool, - projectDonationsSqrtRootSum: - secondProject.estimatedMatching.projectDonationsSqrtRootSum, - allProjectsSum: secondProject.estimatedMatching.allProjectsSum, - }); - - assert.equal(Math.floor(project1EstimatedMatching), 666); - assert.equal(Math.floor(project2EstimatedMatching), 333); - qfRound.isActive = false; - await qfRound.save(); - }); - - it('should return projects, filter by ActiveQfRound', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - verified: true, - listed: true, - }); - - const qfRound = await QfRound.create({ - isActive: true, - name: 'test', - allocatedFund: 100, - slug: new Date().getTime().toString(), - minimumPassportScore: 10, - beginDate: new Date(), - endDate: new Date(), - }).save(); - project.qfRounds = [qfRound]; - await project.save(); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - limit: 50, - skip: 0, - filters: ['ActiveQfRound'], - }, - }); - assert.equal(result.data.data.allProjects.projects.length, 1); - qfRound.isActive = false; - await qfRound.save(); - }); - - it('should return projects, filter by ActiveQfRound, and not return non verified projects', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - verified: false, - listed: true, - }); - - const qfRound = await QfRound.create({ - isActive: true, - name: 'test', - allocatedFund: 100, - slug: new Date().getTime().toString(), - minimumPassportScore: 10, - beginDate: new Date(), - endDate: new Date(), - }).save(); - project.qfRounds = [qfRound]; - await project.save(); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - limit: 50, - skip: 0, - filters: ['ActiveQfRound', 'Verified'], - }, - }); - assert.equal(result.data.data.allProjects.projects.length, 0); - qfRound.isActive = false; - await qfRound.save(); - }); - it('should return projects, filter by ActiveQfRound, and not return non listed projects', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - verified: true, - listed: false, - reviewStatus: ReviewStatus.NotListed, - }); - - const qfRound = await QfRound.create({ - isActive: true, - name: 'test', - allocatedFund: 100, - slug: new Date().getTime().toString(), - minimumPassportScore: 10, - beginDate: new Date(), - endDate: new Date(), - }).save(); - project.qfRounds = [qfRound]; - await project.save(); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - limit: 50, - skip: 0, - filters: ['ActiveQfRound', 'Verified'], - }, - }); - assert.equal(result.data.data.allProjects.projects.length, 0); - qfRound.isActive = false; - await qfRound.save(); - }); - - it('should return empty list when qfRound is not active, filter by ActiveQfRound', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - verified: true, - listed: true, - }); - - const qfRound = await QfRound.create({ - isActive: false, - name: 'test2', - allocatedFund: 100, - slug: new Date().getTime().toString(), - minimumPassportScore: 10, - beginDate: new Date(), - endDate: new Date(), - }).save(); - project.qfRounds = [qfRound]; - await project.save(); - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - limit: 50, - skip: 0, - filters: ['ActiveQfRound'], - }, - }); - - assert.equal(result.data.data.allProjects.projects.length, 0); - qfRound.isActive = false; - await qfRound.save(); - }); -} diff --git a/src/resolvers/projectResolver.test.ts b/src/resolvers/projectResolver.test.ts deleted file mode 100644 index bc131740a..000000000 --- a/src/resolvers/projectResolver.test.ts +++ /dev/null @@ -1,5670 +0,0 @@ -import { assert, expect } from 'chai'; -import 'mocha'; -import axios from 'axios'; -import moment from 'moment'; -import { ArgumentValidationError } from 'type-graphql'; -import { - createProjectData, - generateRandomEtheriumAddress, - generateRandomSolanaAddress, - generateTestAccessToken, - graphqlUrl, - saveFeaturedProjectDirectlyToDb, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, - SEED_DATA, -} from '../../test/testUtils'; -import { - activateProjectQuery, - addProjectUpdateQuery, - addRecipientAddressToProjectQuery, - createProjectQuery, - deactivateProjectQuery, - deleteProjectUpdateQuery, - editProjectUpdateQuery, - fetchFeaturedProjects, - fetchFeaturedProjectUpdate, - fetchLatestProjectUpdates, - fetchLikedProjectsQuery, - fetchMultiFilterAllProjectsQuery, - fetchNewProjectsPerDate, - fetchProjectBySlugQuery, - fetchProjectUpdatesQuery, - fetchSimilarProjectsBySlugQuery, - getProjectsAcceptTokensQuery, - getPurpleList, - projectByIdQuery, - projectsByUserIdQuery, - updateProjectQuery, - walletAddressIsPurpleListed, - walletAddressIsValid, -} from '../../test/graphqlQueries'; -import { CreateProjectInput, UpdateProjectInput } from './types/project-input'; -import { - errorMessages, - i18n, - translationErrorMessagesKeys, -} from '../utils/errorMessages'; -import { - Project, - ProjectUpdate, - ProjStatus, - ReviewStatus, - RevokeSteps, -} from '../entities/project'; -import { Category } from '../entities/category'; -import { Reaction } from '../entities/reaction'; -import { ProjectStatus } from '../entities/projectStatus'; -import { User } from '../entities/user'; -import { Organization, ORGANIZATION_LABELS } from '../entities/organization'; -import { Token } from '../entities/token'; -import { NETWORK_IDS } from '../provider'; -import { - addNewProjectAddress, - findAllRelatedAddressByWalletAddress, - removeRecipientAddressOfProject, -} from '../repositories/projectAddressRepository'; -import { - PROJECT_VERIFICATION_STATUSES, - ProjectVerificationForm, -} from '../entities/projectVerificationForm'; -import { MainCategory } from '../entities/mainCategory'; -import { findOneProjectStatusHistoryByProjectId } from '../repositories/projectSatusHistoryRepository'; -import { setPowerRound } from '../repositories/powerRoundRepository'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from '../repositories/powerBoostingRepository'; -import { - refreshProjectFuturePowerView, - refreshProjectPowerView, -} from '../repositories/projectPowerViewRepository'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { ProjectAddress } from '../entities/projectAddress'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { refreshUserProjectPowerView } from '../repositories/userProjectPowerViewRepository'; -import { AppDataSource } from '../orm'; -// We are using cache so redis needs to be cleared for tests with same filters -import { - Campaign, - CampaignFilterField, - CampaignSortingField, - CampaignType, -} from '../entities/campaign'; -import { generateRandomString } from '../utils/utils'; -import { FeaturedUpdate } from '../entities/featuredUpdate'; -import { - PROJECT_DESCRIPTION_MAX_LENGTH, - PROJECT_TITLE_MAX_LENGTH, -} from '../constants/validators'; -import { InstantPowerBalance } from '../entities/instantPowerBalance'; -import { saveOrUpdateInstantPowerBalances } from '../repositories/instantBoostingRepository'; -import { updateInstantBoosting } from '../services/instantBoostingServices'; -import { addOrUpdatePowerSnapshotBalances } from '../repositories/powerBalanceSnapshotRepository'; -import { findPowerSnapshots } from '../repositories/powerSnapshotRepository'; -import { cacheProjectCampaigns } from '../services/campaignService'; -import { ChainType } from '../types/network'; -import { QfRound } from '../entities/qfRound'; - -const ARGUMENT_VALIDATION_ERROR_MESSAGE = new ArgumentValidationError([ - { property: '' }, -]).message; - -describe('createProject test cases --->', createProjectTestCases); -describe('updateProject test cases --->', updateProjectTestCases); -describe( - 'addRecipientAddressToProject test cases --->', - addRecipientAddressToProjectTestCases, -); -describe('projectsByUserId test cases --->', projectsByUserIdTestCases); - -describe('deactivateProject test cases --->', deactivateProjectTestCases); -describe('activateProject test cases --->', activateProjectTestCases); - -describe('getProjectUpdates test cases --->', getProjectUpdatesTestCases); -describe( - 'likedProjectsByUserId test cases --->', - likedProjectsByUserIdTestCases, -); -describe('projectBySlug test cases --->', projectBySlugTestCases); -describe('projectById test cases --->', projectByIdTestCases); -describe('getPurpleList test cases --->', getPurpleListTestCases); -describe( - 'walletAddressIsPurpleListed Test Cases --->', - walletAddressIsPurpleListedTestCases, -); - -describe('featureProjectsTestCases --->', featureProjectsTestCases); -describe('featureProjectUpdateTestCases --->', featuredProjectUpdateTestCases); - -describe('walletAddressIsValid test cases --->', walletAddressIsValidTestCases); -// TODO We should implement test cases for below query/mutation -// describe('topProjects test cases --->', topProjectsTestCases); -// describe('project test cases --->', projectTestCases); -// describe('uploadImage test cases --->', uploadImageTestCases); -describe('addProjectUpdate test cases --->', addProjectUpdateTestCases); -describe('editProjectUpdate test cases --->', editProjectUpdateTestCases); -describe('deleteProjectUpdate test cases --->', deleteProjectUpdateTestCases); -// describe('getProjectsRecipients test cases --->', getProjectsRecipientsTestCases); -// describe('getProjectReactions test cases --->', getProjectReactionsTestCases); -// describe('isValidTitleForProject test cases --->', isValidTitleForProjectTestCases); -// describe('projectByAddress test cases --->', projectByAddressTestCases); -describe( - 'likedProjectsByUserId test cases --->', - likedProjectsByUserIdTestCases, -); -describe( - 'similarProjectsBySlug test cases --->', - similarProjectsBySlugTestCases, -); - -describe('projectSearch test cases --->', projectSearchTestCases); - -describe('projectUpdates query test cases --->', projectUpdatesTestCases); - -describe( - 'getProjectsAcceptTokens() test cases --->', - getProjectsAcceptTokensTestCases, -); -// We may can delete this query -// describe('updateProjectStatus test cases --->', updateProjectStatusTestCases); - -// describe('activateProject test cases --->', activateProjectTestCases); - -describe('projectsPerDate() test cases --->', projectsPerDateTestCases); - -function projectsPerDateTestCases() { - it('should projects created in a time range', async () => { - await saveProjectDirectlyToDb({ - ...createProjectData(), - creationDate: moment().add(10, 'days').toDate(), - }); - await saveProjectDirectlyToDb({ - ...createProjectData(), - creationDate: moment().add(44, 'days').toDate(), - }); - const variables = { - fromDate: moment().add(9, 'days').toDate().toISOString().split('T')[0], - toDate: moment().add(45, 'days').toDate().toISOString().split('T')[0], - }; - const projectsResponse = await axios.post(graphqlUrl, { - query: fetchNewProjectsPerDate, - variables, - }); - - assert.isOk(projectsResponse); - assert.equal(projectsResponse.data.data.projectsPerDate.total, 2); - const total = - projectsResponse.data.data.projectsPerDate.totalPerMonthAndYear.reduce( - (sum, value) => sum + value.total, - 0, - ); - assert.equal(projectsResponse.data.data.projectsPerDate.total, total); - }); -} - -function getProjectsAcceptTokensTestCases() { - it('should return all tokens for giveth projects', async () => { - const project = await saveProjectDirectlyToDb(createProjectData()); - const allTokens = await Token.find({}); - const result = await axios.post(graphqlUrl, { - query: getProjectsAcceptTokensQuery, - variables: { - projectId: project.id, - }, - }); - assert.isOk(result.data.data.getProjectAcceptTokens); - assert.equal( - result.data.data.getProjectAcceptTokens.length, - allTokens.length, - ); - }); - it('should return all tokens for trace projects', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - organizationLabel: ORGANIZATION_LABELS.TRACE, - }); - const traceOrganization = (await Organization.findOne({ - where: { - label: ORGANIZATION_LABELS.TRACE, - }, - })) as Organization; - - const allTokens = ( - await Token.query(` - SELECT COUNT(*) as "tokenCount" - FROM organization_tokens_token - WHERE "organizationId" = ${traceOrganization.id} - `) - )[0]; - const result = await axios.post(graphqlUrl, { - query: getProjectsAcceptTokensQuery, - variables: { - projectId: project.id, - }, - }); - assert.isOk(result.data.data.getProjectAcceptTokens); - assert.equal( - result.data.data.getProjectAcceptTokens.length, - Number(allTokens.tokenCount), - ); - }); - it('should return just Gnosis tokens when project just have Gnosis recipient address', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - organizationLabel: ORGANIZATION_LABELS.TRACE, - networkId: NETWORK_IDS.XDAI, - }); - - const result = await axios.post(graphqlUrl, { - query: getProjectsAcceptTokensQuery, - variables: { - projectId: project.id, - }, - }); - assert.isNotEmpty(result.data.data.getProjectAcceptTokens); - result.data.data.getProjectAcceptTokens.forEach(token => { - assert.equal(token.networkId, NETWORK_IDS.XDAI); - }); - }); - it('should return just Ropsten tokens when project just have Ropsten recipient address', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - organizationLabel: ORGANIZATION_LABELS.TRACE, - networkId: NETWORK_IDS.ROPSTEN, - }); - - const result = await axios.post(graphqlUrl, { - query: getProjectsAcceptTokensQuery, - variables: { - projectId: project.id, - }, - }); - assert.isNotEmpty(result.data.data.getProjectAcceptTokens); - result.data.data.getProjectAcceptTokens.forEach(token => { - assert.equal(token.networkId, NETWORK_IDS.ROPSTEN); - }); - }); - it('should return just Solana and Ropsten tokens when project just have Solana and Ropsten recipient address', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - organizationLabel: ORGANIZATION_LABELS.TRACE, - networkId: NETWORK_IDS.ROPSTEN, - }); - - await addNewProjectAddress({ - project, - user: project.adminUser, - isRecipient: true, - networkId: NETWORK_IDS.SOLANA_MAINNET, - address: generateRandomSolanaAddress(), - chainType: ChainType.SOLANA, - }); - - const result = await axios.post(graphqlUrl, { - query: getProjectsAcceptTokensQuery, - variables: { - projectId: project.id, - }, - }); - assert.isNotEmpty(result.data.data.getProjectAcceptTokens); - result.data.data.getProjectAcceptTokens.forEach(token => { - expect(token.networkId).to.satisfy( - networkId => - networkId === NETWORK_IDS.SOLANA_MAINNET || - networkId === NETWORK_IDS.ROPSTEN, - ); - }); - }); - it('should no tokens when there is not any recipient address', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - organizationLabel: ORGANIZATION_LABELS.TRACE, - networkId: NETWORK_IDS.ROPSTEN, - }); - await removeRecipientAddressOfProject({ project }); - - const result = await axios.post(graphqlUrl, { - query: getProjectsAcceptTokensQuery, - variables: { - projectId: project.id, - }, - }); - assert.isEmpty(result.data.data.getProjectAcceptTokens); - }); -} - -function projectsByUserIdTestCases() { - it('should return projects with verificationForm if userId is same as logged in user', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - adminUserId: user.id, - }); - - const verificationForm = await ProjectVerificationForm.create({ - project: project1, - user, - status: PROJECT_VERIFICATION_STATUSES.DRAFT, - }).save(); - - const accessToken = await generateTestAccessToken(user!.id); - - const result = await axios.post( - graphqlUrl, - { - query: projectsByUserIdQuery, - variables: { - userId: user!.id, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - const projects = result.data.data.projectsByUserId.projects; - const projectWithAnotherOwner = projects.find( - project => project.adminUserId !== user!.id, - ); - assert.isNotOk(projectWithAnotherOwner); - projects.forEach(project => { - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isOk(project.projectVerificationForm); - assert.equal(project.projectVerificationForm.id, verificationForm.id); - assert.isNotOk(project.adminUser.email); - assert.equal(project.organization.label, ORGANIZATION_LABELS.GIVETH); - }); - }); - - it('should return projects with specific admin', async () => { - const userId = SEED_DATA.FIRST_USER.id; - const result = await axios.post(graphqlUrl, { - query: projectsByUserIdQuery, - variables: { - userId, - }, - }); - const projects = result.data.data.projectsByUserId.projects; - const projectWithAnotherOwner = projects.find( - project => project.adminUserId !== userId, - ); - assert.isNotOk(projectWithAnotherOwner); - projects.forEach(project => { - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNull(project.projectVerificationForm); - assert.isNotOk(project.adminUser.email); - }); - }); - - it('should return projects with current take', async () => { - const take = 1; - const userId = SEED_DATA.FIRST_USER.id; - const result = await axios.post(graphqlUrl, { - query: projectsByUserIdQuery, - variables: { - take, - userId, - }, - }); - const projects = result.data.data.projectsByUserId.projects; - assert.equal(projects.length, take); - projects.forEach(project => { - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - }); - - it('should return projects with qfRound', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project = await saveProjectDirectlyToDb(createProjectData(), user); - const qfRound = QfRound.create({ - isActive: false, - name: generateRandomString(10), - slug: generateRandomString(10), - allocatedFund: 100, - minimumPassportScore: 8, - beginDate: new Date(), - endDate: moment().add(10, 'days').toDate(), - }); - await qfRound.save(); - project.qfRounds = [qfRound]; - await project.save(); - const accessToken = await generateTestAccessToken(user!.id); - - const result = await axios.post( - graphqlUrl, - { - query: projectsByUserIdQuery, - variables: { - userId: user.id, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - // We have created user just in this test case so this will have only one project - const fetchedProject = result.data.data.projectsByUserId.projects[0]; - assert.equal(fetchedProject.qfRounds[0].id, qfRound.id); - }); - - it('should not return draft projects', async () => { - const take = 1; - const userId = SEED_DATA.FIRST_USER.id; - const draftProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: SEED_DATA.FIRST_USER.id, - statusId: ProjStatus.drafted, - }); - const result = await axios.post(graphqlUrl, { - query: projectsByUserIdQuery, - variables: { - take, - userId, - }, - }); - const projects = result.data.data.projectsByUserId.projects; - assert.equal(projects.length, take); - assert.isNotOk(projects.find(project => project.id === draftProject.id)); - projects.forEach(project => { - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - }); - - it('should not return not listed projects', async () => { - const userId = SEED_DATA.FIRST_USER.id; - const notListedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: SEED_DATA.FIRST_USER.id, - listed: false, - reviewStatus: ReviewStatus.NotListed, - }); - const result = await axios.post(graphqlUrl, { - query: projectsByUserIdQuery, - variables: { - userId, - }, - }); - const projects = result.data.data.projectsByUserId.projects; - assert.isNotOk( - projects.find(project => project.id === notListedProject.id), - ); - projects.forEach(project => { - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - }); - - it('should not return new created active project', async () => { - const userId = SEED_DATA.FIRST_USER.id; - const activeProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - }); - const result = await axios.post(graphqlUrl, { - query: projectsByUserIdQuery, - variables: { - userId, - }, - }); - const projects = result.data.data.projectsByUserId.projects; - assert.equal(projects[0].id, activeProject.id); - projects.forEach(project => { - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - assert.isNotEmpty(result.data.data.projectsByUserId.projects[0].addresses); - }); -} - -function createProjectTestCases() { - it('should not create projects with same slug and title', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const sampleProject1 = { - title: 'title1', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const sampleProject2 = { - title: 'title1', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const res1 = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject1, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - const res2 = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject2, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - const isRes1Ok = !!res1.data.data?.createProject; - const isRes2Ok = !!res2.data.data?.createProject; - assert.isTrue(isRes1Ok); - assert.isFalse(isRes2Ok); - }); - it('Create Project should return <>, calling without token IN ENGLISH when no-lang header is sent', async () => { - const sampleProject = { - title: 'title1', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - {}, - ); - - assert.equal(result.status, 200); - // default is english so it will match - assert.equal( - result.data.errors[0].message, - i18n.__(translationErrorMessagesKeys.AUTHENTICATION_REQUIRED), - ); - }); - it('Create Project should return <>, calling without token IN ENGLISH when non supported language is sent', async () => { - const sampleProject = { - title: 'title1', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - 'accept-language': 'br', - }, - }, - ); - - assert.equal(result.status, 200); - // default is english so it will match - assert.equal( - result.data.errors[0].message, - i18n.__(translationErrorMessagesKeys.AUTHENTICATION_REQUIRED), - ); - }); - it('Create Project should return <>, calling without token IN SPANISH', async () => { - const sampleProject = { - title: 'title1', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - 'accept-language': 'es', - }, - }, - ); - i18n.setLocale('es'); // for the test translation scope - assert.equal(result.status, 200); - assert.equal( - result.data.errors[0].message, - i18n.__(translationErrorMessagesKeys.AUTHENTICATION_REQUIRED), - ); - }); - it('Create Project should return <>, calling without token', async () => { - const sampleProject = { - title: 'title1', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - 'accept-language': 'en', - }, - }, - ); - - assert.equal(result.status, 200); - assert.equal( - result.data.errors[0].message, - errorMessages.AUTHENTICATION_REQUIRED, - ); - }); - it('Should get error, invalid category', async () => { - const sampleProject: CreateProjectInput = { - title: String(new Date().getTime()), - categories: ['invalid category'], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - result.data.errors[0].message, - errorMessages.CATEGORIES_MUST_BE_FROM_THE_FRONTEND_SUBSELECTION, - ); - }); - it('Should get error, when selected category is not active', async () => { - const mainCategory = await MainCategory.findOne({ where: {} }); - const nonActiveCategory = await Category.create({ - name: 'nonActiveCategory', - value: 'nonActiveCategory', - isActive: false, - source: 'adhoc', - mainCategory: mainCategory as MainCategory, - }).save(); - const sampleProject: CreateProjectInput = { - title: String(new Date().getTime()), - categories: [nonActiveCategory.name], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - result.data.errors[0].message, - errorMessages.CATEGORIES_MUST_BE_FROM_THE_FRONTEND_SUBSELECTION, - ); - }); - it('Should get error, when selected category canUseOnFrontend is false', async () => { - const mainCategory = await MainCategory.findOne({ where: {} }); - const nonActiveCategory = await Category.create({ - name: new Date().toISOString(), - value: new Date().toISOString(), - isActive: true, - canUseOnFrontend: false, - source: 'adhoc', - mainCategory: mainCategory as MainCategory, - }).save(); - const sampleProject: CreateProjectInput = { - title: String(new Date().getTime()), - categories: [nonActiveCategory.name], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - result.data.errors[0].message, - errorMessages.CATEGORIES_MUST_BE_FROM_THE_FRONTEND_SUBSELECTION, - ); - }); - it('Should get error, when more than 5 categories sent', async () => { - const sampleProject: CreateProjectInput = { - title: String(new Date().getTime()), - categories: SEED_DATA.FOOD_SUB_CATEGORIES, - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - chainType: ChainType.EVM, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.MAIN_NET, - chainType: ChainType.EVM, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - result.data.errors[0].message, - errorMessages.CATEGORIES_LENGTH_SHOULD_NOT_BE_MORE_THAN_FIVE, - ); - }); - it('Should not get error, when sending one recipient address', async () => { - const sampleProject: CreateProjectInput = { - title: String(new Date().getTime()), - categories: [ - SEED_DATA.FOOD_SUB_CATEGORIES[0], - SEED_DATA.FOOD_SUB_CATEGORIES[1], - ], - description: '
Sample Project Creation
', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isOk(result.data.data.createProject); - assert.equal( - result.data.data.createProject.descriptionSummary, - 'Sample Project Creation', - ); - }); - it('Should not get error, when sending more thant two recipient address', async () => { - const sampleProject: CreateProjectInput = { - title: String(new Date().getTime()), - categories: [ - SEED_DATA.FOOD_SUB_CATEGORIES[0], - SEED_DATA.FOOD_SUB_CATEGORIES[1], - ], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.MAIN_NET, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.BSC, - }, - { - address: generateRandomSolanaAddress(), - networkId: 0, - chainType: ChainType.SOLANA, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.isOk(result.data.data.createProject); - }); - it('Should get error, when address is not valid value - Ethereum', async () => { - const sampleProject: CreateProjectInput = { - title: String(new Date().getTime()), - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: SEED_DATA.MALFORMED_ETHEREUM_ADDRESS, - networkId: NETWORK_IDS.XDAI, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: { ...sampleProject }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - result.data.errors[0].message, - errorMessages.INVALID_WALLET_ADDRESS, - ); - }); - it('Should get error, when address is not valid value - Solana', async () => { - const sampleProject: CreateProjectInput = { - title: String(new Date().getTime()), - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: SEED_DATA.MALFORMED_SOLANA_ADDRESS, - networkId: 0, - chainType: ChainType.SOLANA, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: { ...sampleProject }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - result.data.errors[0].message, - errorMessages.INVALID_WALLET_ADDRESS, - ); - }); - it('Should get error, when walletAddress of project is repetitive', async () => { - const sampleProject: CreateProjectInput = { - title: String(new Date().getTime()), - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: SEED_DATA.FIRST_PROJECT.walletAddress, - networkId: NETWORK_IDS.XDAI, - }, - { - address: SEED_DATA.FIRST_PROJECT.walletAddress, - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const addProjectResponse = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: { ...sampleProject, title: String(new Date().getTime()) }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - addProjectResponse.data.errors[0].message, - `Address ${SEED_DATA.FIRST_PROJECT.walletAddress} is already being used for a project`, - ); - }); - it('should create project when walletAddress of project is a smart contract address', async () => { - const sampleProject: CreateProjectInput = { - title: String(new Date().getTime()), - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: SEED_DATA.DAI_SMART_CONTRACT_ADDRESS, - networkId: NETWORK_IDS.XDAI, - }, - { - address: SEED_DATA.DAI_SMART_CONTRACT_ADDRESS, - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const addProjectResponse = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: { ...sampleProject }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isOk(addProjectResponse.data.data.createProject); - assert.equal( - addProjectResponse.data.data.createProject.title, - sampleProject.title, - ); - }); - it('Should get error, when title of project is repetitive', async () => { - const sampleProject: CreateProjectInput = { - title: SEED_DATA.FIRST_PROJECT.title, - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const addProjectResponse = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: { - ...sampleProject, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - addProjectResponse.data.errors[0].message, - errorMessages.PROJECT_WITH_THIS_TITLE_EXISTS, - ); - }); - - it('Should get error on too long description and title', async () => { - const sampleProject: CreateProjectInput = { - title: 'title ' + new Date().getTime(), - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - // Too long description - description: 'a'.repeat(PROJECT_DESCRIPTION_MAX_LENGTH + 1), - image: - 'https://gateway.pinata.cloud/ipfs/QmauSzWacQJ9rPkPJgr3J3pdgfNRGAaDCr1yAToVWev2QS', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - let result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - result.data.errors[0].message, - ARGUMENT_VALIDATION_ERROR_MESSAGE, - ); - - // too long title - sampleProject.title = 'a'.repeat(PROJECT_TITLE_MAX_LENGTH + 1); - sampleProject.description = 'description'; - result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - result.data.errors[0].message, - ARGUMENT_VALIDATION_ERROR_MESSAGE, - ); - }); - - it('Should create successfully', async () => { - const sampleProject: CreateProjectInput = { - title: 'title ' + new Date().getTime(), - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - description: 'description', - image: - 'https://gateway.pinata.cloud/ipfs/QmauSzWacQJ9rPkPJgr3J3pdgfNRGAaDCr1yAToVWev2QS', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.exists(result.data); - assert.exists(result.data.data); - assert.exists(result.data.data.createProject); - assert.equal(result.data.data.createProject.title, sampleProject.title); - assert.equal( - result.data.data.createProject.organization.label, - ORGANIZATION_LABELS.GIVETH, - ); - - // When creating project, listed is null by default - assert.equal(result.data.data.createProject.listed, null); - assert.equal( - result.data.data.createProject.reviewStatus, - ReviewStatus.NotReviewed, - ); - - assert.equal( - result.data.data.createProject.adminUser.id, - SEED_DATA.FIRST_USER.id, - ); - assert.equal(result.data.data.createProject.verified, false); - assert.equal( - result.data.data.createProject.status.id, - String(ProjStatus.active), - ); - assert.equal( - result.data.data.createProject.description, - sampleProject.description, - ); - - assert.equal( - result.data.data.createProject.adminUser.walletAddress, - SEED_DATA.FIRST_USER.walletAddress, - ); - assert.equal(result.data.data.createProject.image, sampleProject.image); - assert.equal( - result.data.data.createProject.creationDate, - result.data.data.createProject.updatedAt, - ); - assert.equal(result.data.data.createProject.addresses.length, 2); - assert.equal( - result.data.data.createProject.addresses[0].address, - sampleProject.addresses[0].address, - ); - assert.equal( - result.data.data.createProject.addresses[0].chainType, - ChainType.EVM, - ); - }); - it('Should create successfully with special characters in title', async () => { - const titleWithoutSpecialCharacters = 'title-_' + new Date().getTime(); - const sampleProject: CreateProjectInput = { - title: titleWithoutSpecialCharacters + `?!@#$%^&*+=.|/<">'` + '`', - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - description: 'description', - image: - 'https://gateway.pinata.cloud/ipfs/QmauSzWacQJ9rPkPJgr3J3pdgfNRGAaDCr1yAToVWev2QS', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.exists(result.data); - assert.exists(result.data.data); - assert.exists(result.data.data.createProject); - assert.equal(result.data.data.createProject.title, sampleProject.title); - assert.equal( - result.data.data.createProject.slug, - titleWithoutSpecialCharacters, - ); - }); - - it('Should create draft successfully', async () => { - const sampleProject: CreateProjectInput = { - title: 'draftTitle1 ' + new Date().getTime(), - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - description: 'description', - isDraft: true, - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const result = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.exists(result.data); - assert.exists(result.data.data); - assert.exists(result.data.data.createProject); - assert.equal(result.data.data.createProject.title, sampleProject.title); - assert.equal( - result.data.data.createProject.organization.label, - ORGANIZATION_LABELS.GIVETH, - ); - - // When creating project, listed is null by default - assert.equal(result.data.data.createProject.listed, null); - assert.equal( - result.data.data.createProject.reviewStatus, - ReviewStatus.NotReviewed, - ); - - assert.equal( - result.data.data.createProject.adminUserId, - SEED_DATA.FIRST_USER.id, - ); - assert.equal(result.data.data.createProject.verified, false); - assert.equal( - result.data.data.createProject.status.id, - String(ProjStatus.drafted), - ); - assert.equal( - result.data.data.createProject.description, - sampleProject.description, - ); - }); -} - -function updateProjectTestCases() { - it('Update Project should return <>, calling without token', async () => { - const result = await axios.post(graphqlUrl, { - query: updateProjectQuery, - variables: { - projectId: 1, - newProjectData: { - title: String(new Date().getTime()), - }, - }, - }); - - assert.equal(result.status, 200); - assert.equal( - result.data.errors[0].message, - errorMessages.AUTHENTICATION_REQUIRED, - ); - }); - it('Should get error when updating someone else project', async () => { - const secondUserAccessToken = await generateTestAccessToken( - SEED_DATA.SECOND_USER.id, - ); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: Number(SEED_DATA.FIRST_PROJECT.id), - newProjectData: { - title: String(new Date().getTime()), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${secondUserAccessToken}`, - }, - }, - ); - assert.equal( - editProjectResult.data.errors[0].message, - errorMessages.YOU_ARE_NOT_THE_OWNER_OF_PROJECT, - ); - }); - it('Should get error when project not found', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - // A number that we can be sure there is not a project with this id - projectId: 1_000_000, - newProjectData: { - title: String(new Date().getTime()), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - editProjectResult.data.errors[0].message, - errorMessages.PROJECT_NOT_FOUND, - ); - }); - it('Should get error when sending more than 5 categories', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: Number(SEED_DATA.FIRST_PROJECT.id), - newProjectData: { - title: String(new Date().getTime()), - categories: SEED_DATA.FOOD_SUB_CATEGORIES, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - editProjectResult.data.errors[0].message, - - errorMessages.CATEGORIES_LENGTH_SHOULD_NOT_BE_MORE_THAN_FIVE, - ); - }); - it('Should get error when sent walletAddress is repetitive', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: Number(SEED_DATA.FIRST_PROJECT.id), - newProjectData: { - addresses: [ - { - address: SEED_DATA.SECOND_PROJECT.walletAddress, - networkId: NETWORK_IDS.XDAI, - }, - { - address: SEED_DATA.SECOND_PROJECT.walletAddress, - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - title: SEED_DATA.FIRST_PROJECT.title, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - editProjectResult.data.errors[0].message, - `Address ${SEED_DATA.SECOND_PROJECT.walletAddress} is already being used for a project`, - ); - }); - it('Should update project when sent walletAddress is smartContractAddress', async () => { - await ProjectAddress.createQueryBuilder() - .delete() - .from(ProjectAddress) - .where('address = :address', { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - }) - .execute(); - await Project.createQueryBuilder() - .delete() - .from(Project) - .where('walletAddress = :address', { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - }) - .execute(); - - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - }); - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - addresses: [ - { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - networkId: NETWORK_IDS.XDAI, - }, - { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - title: 'NewTestTitle', - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isOk(editProjectResult.data.data.updateProject); - assert.equal( - editProjectResult.data.data.updateProject.title, - 'NewTestTitle', - ); - const walletaddressOfUpdateProject = - await ProjectAddress.createQueryBuilder() - .where('"projectId" = :projectId', { - projectId: Number(editProjectResult.data.data.updateProject.id), - }) - .getOne(); - assert.isOk( - walletaddressOfUpdateProject!.address, - SEED_DATA.DAI_SMART_CONTRACT_ADDRESS, - ); - }); - it('Should get error on too long description and title', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - - const newProjectData = { - addresses: [ - { - address: newWalletAddress, - networkId: NETWORK_IDS.XDAI, - }, - { - address: newWalletAddress, - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - title: `test title update addresses`, - // Too long description - description: 'a'.repeat(PROJECT_DESCRIPTION_MAX_LENGTH + 1), - }; - - let editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - editProjectResult.data.errors[0].message, - ARGUMENT_VALIDATION_ERROR_MESSAGE, - ); - - // Too long title - newProjectData.description = 'description'; - newProjectData.title = 'a'.repeat(PROJECT_TITLE_MAX_LENGTH + 1); - - editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - editProjectResult.data.errors[0].message, - ARGUMENT_VALIDATION_ERROR_MESSAGE, - ); - }); - - it('Should update addresses successfully - Ethereum', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - addresses: [ - { - address: newWalletAddress, - networkId: NETWORK_IDS.XDAI, - }, - { - address: newWalletAddress, - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - title: `test title update addresses` + new Date().getTime(), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - const { updateProject } = editProjectResult.data.data; - assert.isOk(updateProject); - - const { addresses } = updateProject; - assert.lengthOf(addresses, 2); - addresses.forEach(address => { - assert.equal(address.address, newWalletAddress); - assert.equal(address.chainType, ChainType.EVM); - }); - }); - it('Should update addresses successfully - Solana', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const ethAddress = generateRandomEtheriumAddress(); - const solanaAddress = generateRandomSolanaAddress(); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - addresses: [ - { - address: ethAddress, - networkId: NETWORK_IDS.XDAI, - }, - { - address: solanaAddress, - networkId: 0, - chainType: ChainType.SOLANA, - }, - ], - title: `test title update addresses` + new Date().getTime(), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - const { updateProject } = editProjectResult.data.data; - assert.isOk(updateProject); - - const { addresses } = updateProject; - assert.lengthOf(addresses, 2); - assert.ok( - addresses.some( - address => - address.chainType === ChainType.EVM && - address.address === ethAddress.toLocaleLowerCase(), - ), - ); - assert.ok( - addresses.some( - address => - address.chainType === ChainType.SOLANA && - address.address === solanaAddress, - ), - ); - }); - it('Should update addresses with two addresses successfully', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - const newWalletAddress2 = generateRandomEtheriumAddress(); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - addresses: [ - { - address: newWalletAddress, - networkId: NETWORK_IDS.XDAI, - }, - { - address: newWalletAddress2, - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - title: `test title update addresses with two addresses`, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - // assert.equal(JSON.stringify(editProjectResult.data, null, 4), 'hi'); - assert.isOk(editProjectResult.data.data.updateProject); - assert.equal(editProjectResult.data.data.updateProject.addresses.length, 2); - assert.equal( - editProjectResult.data.data.updateProject.addresses[0].address, - newWalletAddress, - ); - assert.equal( - editProjectResult.data.data.updateProject.addresses[0].networkId, - NETWORK_IDS.XDAI, - ); - assert.equal( - editProjectResult.data.data.updateProject.addresses[0].chainType, - ChainType.EVM, - ); - assert.equal( - editProjectResult.data.data.updateProject.addresses[1].address, - newWalletAddress2, - ); - assert.equal( - editProjectResult.data.data.updateProject.addresses[1].networkId, - NETWORK_IDS.MAIN_NET, - ); - assert.equal( - editProjectResult.data.data.updateProject.addresses[1].chainType, - ChainType.EVM, - ); - }); - it('Should update addresses with current addresses successfully', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const walletAddress = generateRandomEtheriumAddress(); - - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - walletAddress, - }); - const newWalletAddress = project.walletAddress; - - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - addresses: [ - { - address: newWalletAddress, - networkId: NETWORK_IDS.XDAI, - }, - { - address: newWalletAddress, - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - title: `test title update addresses with current addresses`, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - // assert.equal(JSON.stringify(editProjectResult.data, null, 4), 'hi'); - assert.isOk(editProjectResult.data.data.updateProject); - assert.equal(editProjectResult.data.data.updateProject.addresses.length, 2); - assert.equal( - editProjectResult.data.data.updateProject.addresses[0].address, - newWalletAddress, - ); - assert.equal( - editProjectResult.data.data.updateProject.addresses[0].networkId, - NETWORK_IDS.XDAI, - ); - assert.equal( - editProjectResult.data.data.updateProject.addresses[1].address, - newWalletAddress, - ); - assert.equal( - editProjectResult.data.data.updateProject.addresses[1].networkId, - NETWORK_IDS.MAIN_NET, - ); - const queriedAddress = await findAllRelatedAddressByWalletAddress( - newWalletAddress as string, - ); - assert.equal(queriedAddress.length, 2); - }); - it('Should not throw error when sending one address', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - addresses: [ - { - address: newWalletAddress, - networkId: NETWORK_IDS.XDAI, - }, - ], - title: `test title should not throw error when sending one address`, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isOk(editProjectResult.data.data.updateProject); - }); - it('Should not throw error when sending three address', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - addresses: [ - { - address: newWalletAddress, - networkId: NETWORK_IDS.XDAI, - }, - { - address: newWalletAddress, - networkId: NETWORK_IDS.MAIN_NET, - }, - { - address: newWalletAddress, - networkId: NETWORK_IDS.BSC, - }, - ], - title: `test title should throw error when sending three address`, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isOk(editProjectResult.data.data.updateProject); - }); - it('Should get error when sent title is repetitive', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: Number(SEED_DATA.FIRST_PROJECT.id), - newProjectData: { - title: SEED_DATA.SECOND_PROJECT.title, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - editProjectResult.data.errors[0].message, - errorMessages.PROJECT_WITH_THIS_TITLE_EXISTS, - ); - }); - it('Should update successfully when updating with old title', async () => { - const sampleProject: UpdateProjectInput = { - title: 'test ' + String(new Date().getTime()), - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const createProjectResult = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - const newTitle = sampleProject.title.toUpperCase(); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: Number(createProjectResult.data.data.createProject.id), - newProjectData: { - title: newTitle, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(editProjectResult.data.data.updateProject.title, newTitle); - assert.equal( - editProjectResult.data.data.updateProject.adminUser.walletAddress, - SEED_DATA.FIRST_USER.walletAddress, - ); - }); - it('Should update successfully and slugHistory would contain last slug', async () => { - const title = 'test 123456'; - const slug = 'test-123456'; - const sampleProject: UpdateProjectInput = { - title, - categories: [SEED_DATA.FOOD_SUB_CATEGORIES[0]], - description: 'description', - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.XDAI, - }, - { - address: generateRandomEtheriumAddress(), - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }; - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const createProjectResult = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - const newTitle = `test` + new Date().getTime().toString(); - const newSlug = newTitle; - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: Number(createProjectResult.data.data.createProject.id), - newProjectData: { - title: newTitle, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(editProjectResult.data.data.updateProject.title, newTitle); - assert.equal(editProjectResult.data.data.updateProject.slug, newSlug); - assert.isTrue( - editProjectResult.data.data.updateProject.slugHistory.includes(slug), - ); - }); - it('Should update successfully when sending current walletAddress', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const walletAddress = generateRandomEtheriumAddress(); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - walletAddress, - }); - - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - title: String(new Date().getTime()), - addresses: [ - { - address: walletAddress, - networkId: NETWORK_IDS.XDAI, - }, - { - address: walletAddress, - networkId: NETWORK_IDS.MAIN_NET, - }, - ], - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isOk(editProjectResult.data.data.updateProject); - }); - it('Should update successfully and verified(true) field would not change', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - verified: true, - }); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - title: String(new Date().getTime()), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isTrue(editProjectResult.data.data.updateProject.verified); - }); - it('Should update successfully and verified(false) field would not change', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - verified: false, - }); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - title: String(new Date().getTime()), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isFalse(editProjectResult.data.data.updateProject.verified); - }); - it('Should update successfully listed (true) should becomes null', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - listed: true, - reviewStatus: ReviewStatus.Listed, - }); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - title: String(new Date().getTime()), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(editProjectResult.data.data.updateProject.listed, null); - assert.equal( - editProjectResult.data.data.updateProject.reviewStatus, - ReviewStatus.NotReviewed, - ); - }); - it('Should update successfully listed (false) should becomes null', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - listed: false, - reviewStatus: ReviewStatus.NotListed, - }); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - title: String(new Date().getTime()), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(editProjectResult.data.data.updateProject.listed, null); - assert.equal( - editProjectResult.data.data.updateProject.reviewStatus, - ReviewStatus.NotReviewed, - ); - }); - it('Should update image successfully', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const project = await saveProjectDirectlyToDb(createProjectData()); - const image = - 'https://gateway.pinata.cloud/ipfs/QmauSzWacQJ9rPkPJgr3J3pdgfNRGAaDCr1yAToVWev2QS'; - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - image, - title: new Date().getTime().toString(), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(editProjectResult.data.data.updateProject.image, image); - }); - it('Should update image successfully when sending empty string', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - image: - 'https://gateway.pinata.cloud/ipfs/QmauSzWacQJ9rPkPJgr3J3pdgfNRGAaDCr1yAToVWev2QS', - }); - const image = ''; - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - image, - title: new Date().getTime().toString(), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(editProjectResult.data.data.updateProject.image, image); - }); - it('Should change updatedAt when updating project', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - }); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - title: new Date().getTime().toString(), - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isOk(editProjectResult.data.data.updateProject); - assert.notEqual( - editProjectResult.data.data.updateProject.updatedAt, - project.updatedAt, - ); - }); - it('Should not update image when not sending it', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const image = - 'https://gateway.pinata.cloud/ipfs/QmauSzWacQJ9rPkPJgr3J3pdgfNRGAaDCr1yAToVWev2QS'; - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - image, - }); - const newTitle = new Date().getTime().toString(); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - title: newTitle, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(editProjectResult.data.data.updateProject.image, image); - }); - it('Should not change slug when updating description', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const image = - 'https://gateway.pinata.cloud/ipfs/QmauSzWacQJ9rPkPJgr3J3pdgfNRGAaDCr1yAToVWev2QS'; - const title = new Date().getTime().toString(); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - image, - title, - }); - const newDescription = 'test description haahaaa'; - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - description: newDescription, - title, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(editProjectResult.data.data.updateProject.image, image); - assert.equal(editProjectResult.data.data.updateProject.title, title); - assert.equal(editProjectResult.data.data.updateProject.slug, title); - assert.equal( - editProjectResult.data.data.updateProject.description, - newDescription, - ); - }); - it('Should change slug when updating title', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const image = - 'https://gateway.pinata.cloud/ipfs/QmauSzWacQJ9rPkPJgr3J3pdgfNRGAaDCr1yAToVWev2QS'; - const title = new Date().getTime().toString(); - const newTitle = `${title}new`; - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - image, - title, - }); - const editProjectResult = await axios.post( - graphqlUrl, - { - query: updateProjectQuery, - variables: { - projectId: project.id, - newProjectData: { - title: newTitle, - }, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(editProjectResult.data.data.updateProject.image, image); - assert.equal(editProjectResult.data.data.updateProject.title, newTitle); - assert.equal(editProjectResult.data.data.updateProject.slug, newTitle); - assert.isTrue( - editProjectResult.data.data.updateProject.slugHistory.includes(title), - ); - }); -} - -function addRecipientAddressToProjectTestCases() { - it('addRecipientAddressToProject should return <>, calling without token', async () => { - const result = await axios.post(graphqlUrl, { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: 1, - networkId: NETWORK_IDS.POLYGON, - address: generateRandomEtheriumAddress(), - }, - }); - - assert.equal(result.status, 200); - assert.equal( - result.data.errors[0].message, - errorMessages.AUTHENTICATION_REQUIRED, - ); - }); - it('Should get error when updating someone else project', async () => { - const secondUserAccessToken = await generateTestAccessToken( - SEED_DATA.SECOND_USER.id, - ); - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: Number(SEED_DATA.FIRST_PROJECT.id), - networkId: NETWORK_IDS.POLYGON, - address: generateRandomEtheriumAddress(), - }, - }, - { - headers: { - Authorization: `Bearer ${secondUserAccessToken}`, - }, - }, - ); - assert.equal( - response.data.errors[0].message, - errorMessages.YOU_ARE_NOT_THE_OWNER_OF_PROJECT, - ); - }); - it('Should get error when project not found', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - // A number that we can be sure there is not a project with this id - projectId: 1_000_000, - networkId: NETWORK_IDS.POLYGON, - address: generateRandomEtheriumAddress(), - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - response.data.errors[0].message, - errorMessages.PROJECT_NOT_FOUND, - ); - }); - it('Should get error when sent walletAddress is repetitive', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: Number(SEED_DATA.FIRST_PROJECT.id), - networkId: NETWORK_IDS.POLYGON, - address: SEED_DATA.SECOND_PROJECT.walletAddress, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - response.data.errors[0].message, - `Address ${SEED_DATA.SECOND_PROJECT.walletAddress} is already being used for a project`, - ); - }); - it('Should update project when sent walletAddress is smartContractAddress', async () => { - await ProjectAddress.createQueryBuilder() - .delete() - .from(ProjectAddress) - .where('address = :address', { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - }) - .execute(); - await Project.createQueryBuilder() - .delete() - .from(Project) - .where('walletAddress = :address', { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - }) - .execute(); - - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - }); - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: project.id, - networkId: NETWORK_IDS.POLYGON, - address: generateRandomEtheriumAddress(), - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.isOk(response.data.data.addRecipientAddressToProject); - - const walletaddressOfUpdateProject = - await ProjectAddress.createQueryBuilder() - .where('"projectId" = :projectId', { - projectId: Number(response.data.data.addRecipientAddressToProject.id), - }) - .getMany(); - assert.isOk( - walletaddressOfUpdateProject[1]?.address, - SEED_DATA.DAI_SMART_CONTRACT_ADDRESS, - ); - }); - it('Should add address successfully - EVM', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: project.id, - networkId: NETWORK_IDS.POLYGON, - address: newWalletAddress, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - // assert.equal(JSON.stringify(response.data, null, 4), 'hi'); - assert.isOk(response.data.data.addRecipientAddressToProject); - assert.isOk( - response.data.data.addRecipientAddressToProject.addresses.find( - projectAddress => - projectAddress.address === newWalletAddress && - projectAddress.chainType === ChainType.EVM, - ), - ); - }); - - it('Should add address successfully - EVM', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const newWalletAddress = generateRandomSolanaAddress(); - - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: project.id, - networkId: 0, - address: newWalletAddress, - chainType: ChainType.SOLANA, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - // assert.equal(JSON.stringify(response.data, null, 4), 'hi'); - assert.isOk(response.data.data.addRecipientAddressToProject); - assert.isOk( - response.data.data.addRecipientAddressToProject.addresses.find( - projectAddress => - projectAddress.address === newWalletAddress && - projectAddress.chainType === ChainType.SOLANA, - ), - ); - }); - - it('Should add CELO address successfully', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: project.id, - networkId: NETWORK_IDS.CELO, - address: newWalletAddress, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - // assert.equal(JSON.stringify(response.data, null, 4), 'hi'); - assert.isOk(response.data.data.addRecipientAddressToProject); - assert.isOk( - response.data.data.addRecipientAddressToProject.addresses.find( - projectAddress => projectAddress.address === newWalletAddress, - ), - ); - }); - - it('Should add Arbitrum address successfully', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: project.id, - networkId: NETWORK_IDS.ARBITRUM_MAINNET, - address: newWalletAddress, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - // assert.equal(JSON.stringify(response.data, null, 4), 'hi'); - assert.isOk(response.data.data.addRecipientAddressToProject); - assert.isOk( - response.data.data.addRecipientAddressToProject.addresses.find( - projectAddress => projectAddress.address === newWalletAddress, - ), - ); - }); - - it('Should update successfully listed (true) should not change', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - listed: true, - reviewStatus: ReviewStatus.Listed, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: project.id, - networkId: NETWORK_IDS.POLYGON, - address: newWalletAddress, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - // assert.equal(JSON.stringify(response.data, null, 4), 'hi'); - assert.isOk(response.data.data.addRecipientAddressToProject); - assert.isOk( - response.data.data.addRecipientAddressToProject.addresses.find( - projectAddress => projectAddress.address === newWalletAddress, - ), - ); - - assert.equal(response.data.data.addRecipientAddressToProject.listed, true); - assert.equal( - response.data.data.addRecipientAddressToProject.reviewStatus, - ReviewStatus.Listed, - ); - }); - it('Should update successfully listed (false) should should not change', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - listed: false, - reviewStatus: ReviewStatus.NotListed, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: project.id, - networkId: NETWORK_IDS.POLYGON, - address: newWalletAddress, - chainType: ChainType.EVM, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - // assert.equal(JSON.stringify(response.data, null, 4), 'hi'); - assert.isOk(response.data.data.addRecipientAddressToProject); - assert.isOk( - response.data.data.addRecipientAddressToProject.addresses.find( - projectAddress => projectAddress.address === newWalletAddress, - ), - ); - assert.equal(response.data.data.addRecipientAddressToProject.listed, false); - assert.equal( - response.data.data.addRecipientAddressToProject.reviewStatus, - ReviewStatus.NotListed, - ); - }); - - it('Should not change updatedAt when updating project', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const newWalletAddress = generateRandomEtheriumAddress(); - - const response = await axios.post( - graphqlUrl, - { - query: addRecipientAddressToProjectQuery, - variables: { - projectId: project.id, - networkId: NETWORK_IDS.POLYGON, - address: newWalletAddress, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - // assert.equal(JSON.stringify(response.data, null, 4), 'hi'); - assert.isOk(response.data.data.addRecipientAddressToProject); - assert.equal( - response.data.data.addRecipientAddressToProject.updatedAt, - project.updatedAt.toISOString(), - ); - }); -} - -function deactivateProjectTestCases() { - it('Deactivate Project should return <>, calling without token', async () => { - const result = await axios.post(graphqlUrl, { - query: deactivateProjectQuery, - variables: { - projectId: 1, - }, - }); - - assert.equal(result.status, 200); - assert.equal( - result.data.errors[0].message, - errorMessages.AUTHENTICATION_REQUIRED, - ); - }); - it('Should get error when deactivating someone else project', async () => { - const secondUserAccessToken = await generateTestAccessToken( - SEED_DATA.SECOND_USER.id, - ); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: deactivateProjectQuery, - variables: { - projectId: SEED_DATA.FIRST_PROJECT.id, - }, - }, - { - headers: { - Authorization: `Bearer ${secondUserAccessToken}`, - }, - }, - ); - assert.equal( - deactivateProjectResult.data.errors[0].message, - errorMessages.YOU_DONT_HAVE_ACCESS_TO_DEACTIVATE_THIS_PROJECT, - ); - }); - it('Should get error when project not found', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: deactivateProjectQuery, - variables: { - projectId: 1_000_000, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - deactivateProjectResult.data.errors[0].message, - errorMessages.PROJECT_NOT_FOUND, - ); - }); - - it('Should get error when deactivating cancelled/deactivated project', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb(createProjectData()); - const deactiveStatus = await ProjectStatus.findOne({ - where: { - id: ProjStatus.cancelled, - }, - }); - project.status = deactiveStatus as ProjectStatus; - await project.save(); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: deactivateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal( - deactivateProjectResult.data.errors[0].message, - errorMessages.THIS_PROJECT_IS_CANCELLED_OR_DEACTIVATED_ALREADY, - ); - }); - - it('Should deactivate project successfully', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb(createProjectData()); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: deactivateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(deactivateProjectResult.data.data.deactivateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.deactive); - }); - - it('Should deactivate project successfully and create projectStatusHistory', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb(createProjectData()); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: deactivateProjectQuery, - variables: { - projectId: Number(project.id), - reasonId: 1, - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(deactivateProjectResult.data.data.deactivateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.deactive); - const projectStatusHistory = await findOneProjectStatusHistoryByProjectId( - project.id, - ); - assert.isOk(projectStatusHistory); - assert.equal(projectStatusHistory?.reasonId, 1); - }); - it('Should deactivate project successfully and create projectStatusHistory without reasonId', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb(createProjectData()); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: deactivateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(deactivateProjectResult.data.data.deactivateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.deactive); - const projectStatusHistory = await findOneProjectStatusHistoryByProjectId( - project.id, - ); - assert.isOk(projectStatusHistory); - assert.isNotOk(projectStatusHistory?.reasonId); - }); - - it('Should deactivate project successfully, will affect listed to (false)', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - listed: true, - reviewStatus: ReviewStatus.Listed, - }); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: deactivateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(deactivateProjectResult.data.data.deactivateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.deactive); - assert.equal(updatedProject?.reviewStatus, ReviewStatus.NotListed); - assert.isFalse(updatedProject?.listed); - }); - - it('Should deactivate project successfully, wont affect listed(false)', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - listed: false, - reviewStatus: ReviewStatus.NotListed, - }); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: deactivateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(deactivateProjectResult.data.data.deactivateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.deactive); - assert.isFalse(updatedProject?.listed); - assert.equal(updatedProject?.reviewStatus, ReviewStatus.NotListed); - }); - - it('Should deactivate project successfully, wont affect verified(true)', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - verified: true, - }); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: deactivateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(deactivateProjectResult.data.data.deactivateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.deactive); - assert.isTrue(updatedProject?.verified); - }); - - it('Should deactivate project successfully, wont affect verified(false)', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - verified: false, - }); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: deactivateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(deactivateProjectResult.data.data.deactivateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.deactive); - assert.isFalse(updatedProject?.verified); - }); -} - -function activateProjectTestCases() { - it('Activate Project should return <>, calling without token', async () => { - const result = await axios.post(graphqlUrl, { - query: activateProjectQuery, - variables: { - projectId: 1, - }, - }); - - assert.equal(result.status, 200); - assert.equal( - result.data.errors[0].message, - errorMessages.AUTHENTICATION_REQUIRED, - ); - }); - it('Should get error when activating someone else project', async () => { - const secondUserAccessToken = await generateTestAccessToken( - SEED_DATA.SECOND_USER.id, - ); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: activateProjectQuery, - variables: { - projectId: SEED_DATA.FIRST_PROJECT.id, - }, - }, - { - headers: { - Authorization: `Bearer ${secondUserAccessToken}`, - }, - }, - ); - assert.equal( - deactivateProjectResult.data.errors[0].message, - errorMessages.YOU_DONT_HAVE_ACCESS_TO_DEACTIVATE_THIS_PROJECT, - ); - }); - it('Should get error when project not found', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: activateProjectQuery, - variables: { - projectId: 1_000_000, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - deactivateProjectResult.data.errors[0].message, - errorMessages.PROJECT_NOT_FOUND, - ); - }); - - it('Should get error when Activating cancelled project', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb(createProjectData()); - const deactiveStatus = await ProjectStatus.findOne({ - where: { - id: ProjStatus.cancelled, - }, - }); - project.status = deactiveStatus as ProjectStatus; - await project.save(); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: activateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal( - deactivateProjectResult.data.errors[0].message, - errorMessages.THIS_PROJECT_IS_CANCELLED_OR_DEACTIVATED_ALREADY, - ); - }); - - it('Should activate project successfully', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - statusId: ProjStatus.deactive, - }); - const activateProjectResult = await axios.post( - graphqlUrl, - { - query: activateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(activateProjectResult.data.data.activateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.active); - }); - - it('Should activate draft project successfully', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - statusId: ProjStatus.drafted, - }); - const activateProjectResult = await axios.post( - graphqlUrl, - { - query: activateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(activateProjectResult.data.data.activateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.active); - }); - - it('Should activate project successfully and create projectStatusHistory', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - statusId: ProjStatus.deactive, - }); - const deactivateProjectResult = await axios.post( - graphqlUrl, - { - query: activateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(deactivateProjectResult.data.data.activateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.active); - const projectStatusHistory = await findOneProjectStatusHistoryByProjectId( - project.id, - ); - assert.isOk(projectStatusHistory); - assert.isNotOk(projectStatusHistory?.reasonId); - }); - - it('Should activate project successfully, should change listed from true to null', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - statusId: ProjStatus.deactive, - listed: true, - reviewStatus: ReviewStatus.Listed, - }); - - const activateProjectResult = await axios.post( - graphqlUrl, - { - query: activateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(activateProjectResult.data.data.activateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.active); - assert.isNull(updatedProject?.listed); - assert.equal(updatedProject?.reviewStatus, ReviewStatus.NotReviewed); - }); - - it('Should activate project successfully, should change listed from false to null', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - listed: false, - reviewStatus: ReviewStatus.NotListed, - statusId: ProjStatus.deactive, - }); - const activateProjectResult = await axios.post( - graphqlUrl, - { - query: activateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(activateProjectResult.data.data.activateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.active); - assert.isNull(updatedProject?.listed); - assert.equal(updatedProject?.reviewStatus, ReviewStatus.NotReviewed); - }); - - it('Should activate project successfully, wont affect verified(true)', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - verified: true, - }); - const activateProjectResult = await axios.post( - graphqlUrl, - { - query: activateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(activateProjectResult.data.data.activateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.active); - assert.isTrue(updatedProject?.verified); - }); - - it('Should activate project successfully, wont affect verified(false)', async () => { - const firstUserAccessToken = await generateTestAccessToken( - SEED_DATA.FIRST_USER.id, - ); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - verified: false, - }); - const activateProjectResult = await axios.post( - graphqlUrl, - { - query: activateProjectQuery, - variables: { - projectId: Number(project.id), - }, - }, - { - headers: { - Authorization: `Bearer ${firstUserAccessToken}`, - }, - }, - ); - assert.equal(activateProjectResult.data.data.activateProject, true); - const updatedProject = await Project.findOne({ - where: { - id: project.id, - }, - }); - assert.equal(updatedProject?.statusId, ProjStatus.active); - assert.isFalse(updatedProject?.verified); - }); -} - -function likedProjectsByUserIdTestCases() { - it('should returns projects liked by the user', async () => { - const take = 1; - const result = await axios.post(graphqlUrl, { - query: fetchLikedProjectsQuery, - variables: { - userId: SEED_DATA.FIRST_USER.id, - take, - }, - }); - - const projects = result.data.data.likedProjectsByUserId.projects; - assert.equal(projects.length, take); - projects.forEach(project => { - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - assert.equal(project.organization.label, ORGANIZATION_LABELS.GIVETH); - }); - const reaction = await Reaction.findOne({ - where: { - userId: SEED_DATA.FIRST_USER.id, - projectId: SEED_DATA.FIRST_PROJECT.id, - }, - }); - - assert.equal(projects[0].id, reaction?.projectId); - assert.equal(projects[0]?.reaction?.id, reaction?.id); - }); - describe('if the user did not like any project', () => { - it('should return an empty list', async () => { - const result = await axios.post(graphqlUrl, { - query: fetchLikedProjectsQuery, - variables: { - userId: SEED_DATA.SECOND_USER.id, - }, - }); - - const projects = result.data.data.likedProjectsByUserId.projects; - assert.equal(projects.length, 0); - }); - }); -} - -function walletAddressIsValidTestCases() { - it('should return true for new ethereum address', async () => { - const result = await axios.post(graphqlUrl, { - query: walletAddressIsValid, - variables: { - address: generateRandomEtheriumAddress(), - }, - }); - assert.equal(result.data.data.walletAddressIsValid, true); - }); - - it('should throw error for invalid ethereum address', async () => { - const result = await axios.post(graphqlUrl, { - query: walletAddressIsValid, - variables: { - address: '4297urofklshnforp2', - }, - }); - assert.equal( - result.data.errors[0].message, - errorMessages.INVALID_WALLET_ADDRESS, - ); - }); - - it('should throw error for existing walletAddress', async () => { - const result = await axios.post(graphqlUrl, { - query: walletAddressIsValid, - variables: { - address: SEED_DATA.FIRST_PROJECT.walletAddress, - }, - }); - assert.equal( - result.data.errors[0].message, - `Address ${SEED_DATA.FIRST_PROJECT.walletAddress} is already being used for a project`, - ); - }); - it('should return true if walletAddress is smart contract address in mainnet', async () => { - await ProjectAddress.createQueryBuilder() - .delete() - .from(ProjectAddress) - .where('address = :address', { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - }) - .execute(); - await Project.createQueryBuilder() - .delete() - .from(Project) - .where('walletAddress = :address', { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - }) - .execute(); - - // DAI address https://etherscan.io/token/0x6b175474e89094c44da98b954eedeac495271d0f - const walletAddress = '0x6b175474e89094c44da98b954eedeac495271d0f'; - const result = await axios.post(graphqlUrl, { - query: walletAddressIsValid, - variables: { - address: walletAddress, - }, - }); - assert.equal(result.data.data.walletAddressIsValid, true); - }); - - it('should return true if walletAddress is smart contract address in xdai', async () => { - // GIV address https://blockscout.com/xdai/mainnet/token/0x4f4F9b8D5B4d0Dc10506e5551B0513B61fD59e75/token-transfers - const walletAddress = '0x4f4F9b8D5B4d0Dc10506e5551B0513B61fD59e75'; - const result = await axios.post(graphqlUrl, { - query: walletAddressIsValid, - variables: { - address: walletAddress, - }, - }); - assert.equal(result.data.data.walletAddressIsValid, true); - }); - - it('should throw error for existing walletAddress - upperCase', async () => { - const upperCaseWalletAddress = SEED_DATA.FIRST_PROJECT.walletAddress - ?.toUpperCase() - // This replace is because ethereum wallet address should begin with 0x ad toUpperCase make it corrupted - .replace('0X', '0x') as string; - const result = await axios.post(graphqlUrl, { - query: walletAddressIsValid, - variables: { - address: upperCaseWalletAddress, - }, - }); - assert.equal( - result.data.errors[0].message, - `Address ${upperCaseWalletAddress} is already being used for a project`, - ); - }); - it('should throw error for existing walletAddress - lowerCase', async () => { - const lowerCaseWalletAddress = - SEED_DATA.FIRST_PROJECT.walletAddress?.toLowerCase(); - const result = await axios.post(graphqlUrl, { - query: walletAddressIsValid, - variables: { - address: lowerCaseWalletAddress, - }, - }); - assert.equal( - result.data.errors[0].message, - `Address ${lowerCaseWalletAddress} is already being used for a project`, - ); - }); -} - -function projectByIdTestCases() { - it('should return project with id', async () => { - const project = await saveProjectDirectlyToDb(createProjectData()); - const result = await axios.post(graphqlUrl, { - query: projectByIdQuery, - variables: { - id: project.id, - }, - }); - assert.equal(result.data.data.projectById.id, project.id); - assert.equal(result.data.data.projectById.slug, project.slug); - assert.isNotEmpty(result.data.data.projectById.addresses); - assert.isOk(result.data.data.projectById.adminUser.walletAddress); - assert.isOk(result.data.data.projectById.adminUser.firstName); - assert.isNotOk(result.data.data.projectById.adminUser.email); - assert.isOk(result.data.data.projectById.categories[0].mainCategory.title); - }); - it('should return error for invalid id', async () => { - const result = await axios.post(graphqlUrl, { - query: projectByIdQuery, - variables: { - // To make use id is invalid - id: 9999999, - }, - }); - assert.equal( - result.data.errors[0].message, - errorMessages.PROJECT_NOT_FOUND, - ); - }); - it('should return reaction when user liked the project', async () => { - const project = await saveProjectDirectlyToDb(createProjectData()); - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const reaction = await Reaction.create({ - userId: user.id, - projectId: project.id, - reaction: 'heart', - }).save(); - const result = await axios.post(graphqlUrl, { - query: projectByIdQuery, - variables: { - id: project.id, - connectedWalletUserId: user.id, - }, - }); - assert.equal(result.data.data.projectById.id, project.id); - assert.equal(result.data.data.projectById.reaction.id, reaction.id); - assert.isOk(result.data.data.projectById.adminUser.walletAddress); - assert.isOk(result.data.data.projectById.adminUser.firstName); - assert.isNotOk(result.data.data.projectById.adminUser.email); - }); - it('should not return reaction when user doesnt exist', async () => { - const project = await saveProjectDirectlyToDb(createProjectData()); - const result = await axios.post(graphqlUrl, { - query: projectByIdQuery, - variables: { - id: project.id, - - // To make sure there is no user with that Id - connectedWalletUserId: 9999999, - }, - }); - assert.equal(result.data.data.projectById.id, project.id); - assert.isOk(result.data.data.projectById.adminUser.walletAddress); - assert.isOk(result.data.data.projectById.adminUser.firstName); - assert.isNotOk(result.data.data.projectById.adminUser.email); - assert.isNotOk(result.data.data.projectById.reaction); - }); - it('should not return reaction when user didnt like the project', async () => { - const project = await saveProjectDirectlyToDb(createProjectData()); - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const result = await axios.post(graphqlUrl, { - query: projectByIdQuery, - variables: { - id: project.id, - connectedWalletUserId: user.id, - }, - }); - assert.equal(result.data.data.projectById.id, project.id); - assert.isNotOk(result.data.data.projectById.reaction); - assert.isOk(result.data.data.projectById.adminUser.walletAddress); - assert.isOk(result.data.data.projectById.adminUser.firstName); - assert.isNotOk(result.data.data.projectById.adminUser.email); - }); - it('should not return drafted projects if not logged in', async () => { - const draftedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.drafted, - }); - - const result = await axios.post(graphqlUrl, { - query: projectByIdQuery, - variables: { - id: draftedProject.id, - }, - }); - - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_DONT_HAVE_ACCESS_TO_VIEW_THIS_PROJECT, - ); - }); - it('should return drafted projects of logged in user', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - - const draftedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.drafted, - }); - - const result = await axios.post( - graphqlUrl, - { - query: projectByIdQuery, - variables: { - id: draftedProject.id, - connectedWalletUserId: SEED_DATA.FIRST_USER.id, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - const project = result.data.data.projectById; - assert.equal(Number(project.id), draftedProject.id); - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - it('should not return drafted project is user is logged in but is not owner of project', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.SECOND_USER.id); - - const draftedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.drafted, - }); - - const result = await axios.post( - graphqlUrl, - { - query: projectByIdQuery, - variables: { - id: draftedProject.id, - connectedWalletUserId: SEED_DATA.FIRST_USER.id, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_DONT_HAVE_ACCESS_TO_VIEW_THIS_PROJECT, - ); - }); - - it('should not return cancelled projects if not logged in', async () => { - const cancelledProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.cancelled, - }); - - const result = await axios.post(graphqlUrl, { - query: projectByIdQuery, - variables: { - id: cancelledProject.id, - }, - }); - - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_DONT_HAVE_ACCESS_TO_VIEW_THIS_PROJECT, - ); - }); - it('should return cancelled projects of logged in user', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - - const cancelledProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.cancelled, - }); - - const result = await axios.post( - graphqlUrl, - { - query: projectByIdQuery, - variables: { - id: cancelledProject.id, - connectedWalletUserId: SEED_DATA.FIRST_USER.id, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - const project = result.data.data.projectById; - assert.equal(Number(project.id), cancelledProject.id); - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - it('should not return cancelled project is user is logged in but is not owner of project', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.SECOND_USER.id); - - const cancelledProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.cancelled, - }); - - const result = await axios.post( - graphqlUrl, - { - query: projectByIdQuery, - variables: { - id: cancelledProject.id, - connectedWalletUserId: SEED_DATA.FIRST_USER.id, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_DONT_HAVE_ACCESS_TO_VIEW_THIS_PROJECT, - ); - }); -} - -function walletAddressIsPurpleListedTestCases() { - it('should return true if walletAddress is purpleListed', async () => { - const walletAddress = generateRandomEtheriumAddress(); - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - - await addNewProjectAddress({ - address: walletAddress, - networkId: NETWORK_IDS.XDAI, - project, - user, - }); - const result = await axios.post(graphqlUrl, { - query: walletAddressIsPurpleListed, - variables: { - address: walletAddress, - }, - }); - assert.isTrue(result.data.data.walletAddressIsPurpleListed); - }); - it('should return true if wallet address is from a verifiedProject', async () => { - const walletAddress = generateRandomEtheriumAddress(); - await saveProjectDirectlyToDb({ - ...createProjectData(), - walletAddress, - verified: true, - }); - const result = await axios.post(graphqlUrl, { - query: walletAddressIsPurpleListed, - variables: { - address: walletAddress, - }, - }); - assert.isTrue(result.data.data.walletAddressIsPurpleListed); - }); - it('should return true if sent walletAddress is in upperCase', async () => { - const walletAddress = generateRandomEtheriumAddress(); - await saveProjectDirectlyToDb({ - ...createProjectData(), - walletAddress, - verified: true, - }); - const result = await axios.post(graphqlUrl, { - query: walletAddressIsPurpleListed, - variables: { - address: walletAddress.toUpperCase(), - }, - }); - assert.isTrue(result.data.data.walletAddressIsPurpleListed); - }); - it('should return false if wallet address is from a nonverified project', async () => { - const walletAddress = generateRandomEtheriumAddress(); - await saveProjectDirectlyToDb({ - ...createProjectData(), - walletAddress, - verified: false, - }); - const result = await axios.post(graphqlUrl, { - query: walletAddressIsPurpleListed, - variables: { - address: walletAddress, - }, - }); - assert.isFalse(result.data.data.walletAddressIsPurpleListed); - }); - it('should return false if wallet address is from a nonActive verified project', async () => { - const walletAddress = generateRandomEtheriumAddress(); - await saveProjectDirectlyToDb({ - ...createProjectData(), - walletAddress, - verified: true, - statusId: ProjStatus.drafted, - }); - const result = await axios.post(graphqlUrl, { - query: walletAddressIsPurpleListed, - variables: { - address: walletAddress, - }, - }); - assert.isFalse(result.data.data.walletAddressIsPurpleListed); - }); - it('should return false if its a random non related address', async () => { - const walletAddress = generateRandomEtheriumAddress(); - const result = await axios.post(graphqlUrl, { - query: walletAddressIsPurpleListed, - variables: { - address: walletAddress, - }, - }); - assert.isFalse(result.data.data.walletAddressIsPurpleListed); - }); -} - -function getPurpleListTestCases() { - it('should return purpleAddress records', async () => { - const walletAddress = generateRandomEtheriumAddress(); - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - - await addNewProjectAddress({ - address: walletAddress, - networkId: NETWORK_IDS.XDAI, - project, - user, - }); - const result = await axios.post(graphqlUrl, { - query: getPurpleList, - }); - assert.isOk(result.data.data.getPurpleList); - assert.isTrue( - result.data.data.getPurpleList.includes(walletAddress.toLowerCase()), - ); - }); - it('should return verifiedProject wallet address', async () => { - const walletAddress = generateRandomEtheriumAddress(); - await saveProjectDirectlyToDb({ - ...createProjectData(), - walletAddress, - verified: true, - }); - const result = await axios.post(graphqlUrl, { - query: getPurpleList, - }); - assert.isOk(result.data.data.getPurpleList); - assert.isTrue( - result.data.data.getPurpleList.includes(walletAddress.toLowerCase()), - ); - }); - it('should not return non-verified project wallet address', async () => { - const walletAddress = generateRandomEtheriumAddress(); - await saveProjectDirectlyToDb({ - ...createProjectData(), - walletAddress, - verified: false, - }); - const result = await axios.post(graphqlUrl, { - query: getPurpleList, - }); - assert.isOk(result.data.data.getPurpleList); - assert.isFalse( - result.data.data.getPurpleList.includes(walletAddress.toLowerCase()), - ); - }); -} - -function featuredProjectUpdateTestCases() { - it('should return a specific featured project update', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - reviewStatus: ReviewStatus.Listed, - listed: true, - }); - - const projectUpdate = await ProjectUpdate.create({ - userId: user!.id, - projectId: project.id, - content: 'TestProjectUpdate1', - title: 'testEditProjectUpdate1', - createdAt: new Date(), - isMain: false, - }).save(); - - await saveFeaturedProjectDirectlyToDb( - Number(project.id), - Number(projectUpdate.id), - ); - - const result = await axios.post(graphqlUrl, { - query: fetchFeaturedProjectUpdate, - variables: { - projectId: project.id, - }, - }); - - assert.equal( - Number(result.data.data.featuredProjectUpdate.projectId), - project.id, - ); - assert.equal( - Number(result.data.data.featuredProjectUpdate.id), - projectUpdate.id, - ); - }); -} - -function featureProjectsTestCases() { - before(async () => { - await FeaturedUpdate.clear(); - }); - it('should return all active projects that have been featured', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const settings: [ReviewStatus, ProjStatus][] = [ - [ReviewStatus.Listed, ProjStatus.active], - [ReviewStatus.Listed, ProjStatus.active], - [ReviewStatus.NotListed, ProjStatus.active], // Not listed - [ReviewStatus.NotReviewed, ProjStatus.active], // Not listed - [ReviewStatus.Listed, ProjStatus.deactive], // Not active - ]; - const projects: Project[] = []; - for (const element of settings) { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - reviewStatus: element[0], - statusId: element[1], - }); - projects.push(project); - } - const projectUpdatePromises = projects.map(project => { - return ProjectUpdate.create({ - userId: user!.id, - projectId: project.id, - content: 'TestProjectUpdate', - title: 'testEditProjectUpdate', - createdAt: new Date(), - isMain: false, - }).save(); - }); - const projectUpdates = await Promise.all(projectUpdatePromises); - for (let i = 0; i < projects.length; i++) { - const project = projects[i]; - const projectUpdate = projectUpdates[i]; - const featuredProject = await saveFeaturedProjectDirectlyToDb( - Number(project.id), - Number(projectUpdate.id), - ); - // MUST HAVE POSITION OR WILL NOT BE DISPLAYED - featuredProject.position = i + 1; - await featuredProject.save(); - } - - const take = 10; - const result = await axios.post(graphqlUrl, { - query: fetchFeaturedProjects, - variables: { - take, - }, - }); - - const featuredProjects = result.data.data.featuredProjects.projects; - const totalCount = result.data.data.featuredProjects.totalCount; - - assert.equal(totalCount, 2); - assert.isTrue(featuredProjects[0].featuredUpdate.position === 1); - assert.equal(Number(featuredProjects[0].id), projects[0].id); // Listed and Active - assert.equal(Number(featuredProjects[1].id), projects[1].id); // Listed and Active - }); -} - -function projectUpdatesTestCases() { - it('should return all project updates limited by take and ordered by craetedAt desc', async () => { - const update1Date = moment().add(10, 'days').toDate(); - const update2Date = moment().add(11, 'days').toDate(); - const update3Date = new Date(); - const update4Date = new Date(); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - }); - const project2 = await saveProjectDirectlyToDb({ - ...createProjectData(), - }); - const project3 = await saveProjectDirectlyToDb({ - ...createProjectData(), - statusId: ProjStatus.deactive, - latestUpdateCreationDate: update4Date, - }); - const user = SEED_DATA.FIRST_USER; - - const projectUpdate1 = await ProjectUpdate.create({ - userId: user!.id, - projectId: project.id, - content: 'TestProjectUpdate1', - title: 'testEditProjectUpdate1', - createdAt: update1Date, - isMain: false, - }).save(); - const projectUpdate2 = await ProjectUpdate.create({ - userId: user!.id, - projectId: project2.id, - content: 'TestProjectUpdate2', - title: 'testEditProjectUpdate2', - createdAt: update2Date, - isMain: false, - }).save(); - const projectUpdate3 = await ProjectUpdate.create({ - userId: user!.id, - projectId: project2.id, - content: 'TestProjectUpdateExcluded', - title: 'testEditProjectUpdateExcluded', - createdAt: update3Date, - isMain: false, - }).save(); - const projectUpdate4 = await ProjectUpdate.create({ - userId: user!.id, - projectId: project3.id, - content: 'TestProjectUpdateExcluded', - title: 'testEditProjectUpdateExcluded', - createdAt: update4Date, - isMain: false, - }).save(); - - await Project.update(project.id, { - latestUpdateCreationDate: update1Date, - }); - await Project.update(project2.id, { - latestUpdateCreationDate: update2Date, - }); - - const takeLatestUpdates = 4; // there are other previously created updates - const result = await axios.post(graphqlUrl, { - query: fetchLatestProjectUpdates, - variables: { - takeLatestUpdates, - }, - }); - - assert.isOk(result); - const data = result.data.data.projectUpdates.projectUpdates; - // assert only project's most recent updates are returned - assert.equal(data.length, 2); - for (const pu of data) { - assert.isTrue( - pu.id === projectUpdate1.id || - pu.id === projectUpdate3.id || - pu.id !== projectUpdate2.id || - pu.id !== projectUpdate4.id, - ); - } - // Assert ordered (which matches order of creation) and project data present - assert.isTrue(new Date(data[0].createdAt) > new Date(data[1].createdAt)); - assert.isOk(data[0].project.slug); - assert.equal(data[0].project.slug, project2.slug); - }); -} - -function projectSearchTestCases() { - it('should return projects with a typo in the end of searchTerm', async () => { - const limit = 1; - const USER_DATA = SEED_DATA.FIRST_USER; - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - limit, - // Typo in the title - searchTerm: SEED_DATA.SECOND_PROJECT.title.slice(0, -1) + 'a', - connectedWalletUserId: USER_DATA.id, - }, - }); - - const projects = result.data.data.allProjects.projects; - assert.equal(projects.length, limit); - assert.equal(projects[0].title, SEED_DATA.SECOND_PROJECT.title); - assert.equal(projects[0].slug, SEED_DATA.SECOND_PROJECT.slug); - assert.equal(projects[0].id, SEED_DATA.SECOND_PROJECT.id); - }); - - it('should return projects with the project title inverted in the searchTerm', async () => { - const limit = 1; - const USER_DATA = SEED_DATA.FIRST_USER; - const result = await axios.post(graphqlUrl, { - query: fetchMultiFilterAllProjectsQuery, - variables: { - limit, - searchTerm: SEED_DATA.SECOND_PROJECT.title - .split(' ') - .reverse() - .join(' '), - connectedWalletUserId: USER_DATA.id, - }, - }); - - const projects = result.data.data.allProjects.projects; - assert.equal(projects.length, limit); - assert.equal(projects[0].title, SEED_DATA.SECOND_PROJECT.title); - assert.equal(projects[0].slug, SEED_DATA.SECOND_PROJECT.slug); - assert.equal(projects[0].id, SEED_DATA.SECOND_PROJECT.id); - }); -} - -function getProjectUpdatesTestCases() { - it('should return project updates with current take', async () => { - const take = 2; - const result = await axios.post(graphqlUrl, { - query: fetchProjectUpdatesQuery, - variables: { - projectId: SEED_DATA.FIRST_PROJECT.id, - take, - }, - }); - assert.isOk(result); - const projectUpdates = result.data.data.getProjectUpdates; - assert.equal(projectUpdates.length, take); - }); - - it('should return correct reaction', async () => { - const take = 3; - const result = await axios.post(graphqlUrl, { - query: fetchProjectUpdatesQuery, - variables: { - projectId: SEED_DATA.FIRST_PROJECT.id, - take, - connectedWalletUserId: SEED_DATA.FIRST_USER.id, - }, - }); - assert.isOk(result); - // const projectUpdates: ProjectUpdate[] = result.data.data.getProjectUpdates; - - // const likedProject = projectUpdates.find( - // pu => +pu.id === PROJECT_UPDATE_SEED_DATA.FIRST_PROJECT_UPDATE.id, - // ); - // const noLikedProject = projectUpdates.find( - // pu => +pu.id !== PROJECT_UPDATE_SEED_DATA.FIRST_PROJECT_UPDATE.id, - // ); - - // assert.equal( - // likedProject?.reaction?.id, - // REACTION_SEED_DATA.FIRST_LIKED_PROJECT_UPDATE_REACTION.id, - // ); - // assert.isNull(noLikedProject?.reaction); - }); -} - -function projectBySlugTestCases() { - it('should return projects with indicated slug and verification form status if owner', async () => { - const project1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - const user = (await User.findOne({ - where: { - id: project1.adminUserId, - }, - })) as User; - - const verificationForm = await ProjectVerificationForm.create({ - project: project1, - user, - status: PROJECT_VERIFICATION_STATUSES.DRAFT, - }).save(); - - const accessToken = await generateTestAccessToken(user!.id); - - const result = await axios.post( - graphqlUrl, - { - query: fetchProjectBySlugQuery, - variables: { - slug: project1.slug, - connectedWalletUserId: user!.id, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - const project = result.data.data.projectBySlug; - assert.equal(Number(project.id), project1.id); - assert.isOk(project.verificationFormStatus); - assert.equal(project.verificationFormStatus, verificationForm.status); - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - assert.isOk(project.categories[0].mainCategory.title); - }); - - it('should return projects with indicated slug', async () => { - const walletAddress = generateRandomEtheriumAddress(); - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - const sampleProject1 = { - title: walletAddress, - adminUserId: SEED_DATA.FIRST_USER.id, - addresses: [ - { - address: walletAddress, - networkId: NETWORK_IDS.XDAI, - chainType: ChainType.EVM, - }, - ], - }; - const res1 = await axios.post( - graphqlUrl, - { - query: createProjectQuery, - variables: { - project: sampleProject1, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - const _project = res1.data.data.createProject; - const result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: _project.slug, - }, - }); - const project = result.data.data.projectBySlug; - assert.equal(Number(project.id), Number(_project.id)); - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.givbackFactor); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - assert.isNotEmpty(project.addresses); - assert.equal(project.addresses[0].address, walletAddress); - assert.equal(project.addresses[0].chainType, ChainType.EVM); - }); - - it('should return projects including projectPower', async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBoosting.clear(); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - const walletAddress = generateRandomEtheriumAddress(); - const project1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - walletAddress, - }); - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const roundNumber = project1.id * 10; - await insertSinglePowerBoosting({ - user, - project: project1, - percentage: 10, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user.id, - powerSnapshotId: snapshot.id, - balance: 200, - }); - - await setPowerRound(roundNumber); - - await refreshProjectPowerView(); - const result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: project1.slug, - }, - }); - - const project = result.data.data.projectBySlug; - assert.equal(Number(project.id), project1.id); - - assert.exists(project.projectPower); - assert.isTrue(project.projectPower.totalPower > 0); - }); - - it('should return projects including active ManuallySelected campaigns', async () => { - const projectWithCampaign = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const projectWithoutCampaign = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - - const campaign = await Campaign.create({ - isActive: true, - type: CampaignType.ManuallySelected, - slug: generateRandomString(), - title: 'title1', - description: 'description1', - photo: 'https://google.com', - relatedProjectsSlugs: [projectWithCampaign.slug as string], - order: 1, - }).save(); - const result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: projectWithCampaign.slug, - }, - }); - - const project = result.data.data.projectBySlug; - assert.equal(Number(project.id), projectWithCampaign.id); - - assert.exists(project.campaigns); - assert.isNotEmpty(project.campaigns); - - const projectWithoutCampaignResult = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: projectWithoutCampaign.slug, - }, - }); - - const project2 = projectWithoutCampaignResult.data.data.projectBySlug; - assert.equal(Number(project2.id), projectWithoutCampaign.id); - - assert.isEmpty(project2.campaigns); - - await campaign.remove(); - }); - it('should return projects including active SortField campaigns', async () => { - const projectWithCampaign = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const campaign = await Campaign.create({ - isActive: true, - type: CampaignType.SortField, - sortingField: CampaignSortingField.Newest, - slug: generateRandomString(), - title: 'title1', - description: 'description1', - photo: 'https://google.com', - order: 1, - }).save(); - await cacheProjectCampaigns(); - const result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: projectWithCampaign.slug, - }, - }); - - const project = result.data.data.projectBySlug; - assert.equal(Number(project.id), projectWithCampaign.id); - - assert.exists(project.campaigns); - assert.isNotEmpty(project.campaigns); - assert.equal(project.campaigns[0].id, campaign.id); - - const projectWithoutCampaignResult = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - // and old project that I'm sure it would not be in the Newest campaign - slug: SEED_DATA.FIRST_PROJECT.slug, - }, - }); - - const project2 = projectWithoutCampaignResult.data.data.projectBySlug; - assert.equal(Number(project2.id), SEED_DATA.FIRST_PROJECT.id); - - assert.isEmpty(project2.campaigns); - - await campaign.remove(); - }); - - it('should return projects including active FilterField campaigns (acceptOnGnosis)', async () => { - // In this filter the default sorting for projects is givPower so I need to create a project with power - // to be sure that it will be in the campaign - await PowerBoosting.clear(); - await InstantPowerBalance.clear(); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const projectWithCampaign = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.XDAI, - }); - - const projectWithoutCampaign = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - networkId: NETWORK_IDS.POLYGON, - }); - - await Promise.all( - [[user1, projectWithCampaign, 10]].map(item => { - const [user, project, percentage] = item as [User, Project, number]; - return insertSinglePowerBoosting({ - user, - project, - percentage, - }); - }), - ); - - await saveOrUpdateInstantPowerBalances([ - { - userId: user1.id, - balance: 10000, - balanceAggregatorUpdatedAt: new Date(1_000_000), - }, - ]); - - await updateInstantBoosting(); - - const campaign = await Campaign.create({ - isActive: true, - type: CampaignType.FilterFields, - filterFields: [CampaignFilterField.acceptFundOnGnosis], - slug: generateRandomString(), - title: 'title1', - description: 'description1', - photo: 'https://google.com', - order: 1, - }).save(); - await cacheProjectCampaigns(); - const result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: projectWithCampaign.slug, - }, - }); - - const fetchedProject = result.data.data.projectBySlug; - assert.equal(Number(fetchedProject.id), projectWithCampaign.id); - - assert.exists(fetchedProject.campaigns); - assert.isNotEmpty(fetchedProject.campaigns); - assert.equal(fetchedProject.campaigns[0].id, campaign.id); - - const projectWithoutCampaignResult = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: projectWithoutCampaign.slug, - }, - }); - - const project2 = projectWithoutCampaignResult.data.data.projectBySlug; - assert.equal(Number(project2.id), projectWithoutCampaign.id); - - assert.isEmpty(project2.campaigns); - - await campaign.remove(); - }); - - it('should return projects including active campaigns, even when sent slug is in the slugHistory of project', async () => { - const projectWithCampaign = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - }); - const previousSlug = `${String(new Date().getTime())}-previous`; - projectWithCampaign.slugHistory = [previousSlug]; - await projectWithCampaign.save(); - - const campaign = await Campaign.create({ - isActive: true, - type: CampaignType.ManuallySelected, - slug: generateRandomString(), - title: 'title1', - description: 'description1', - photo: 'https://google.com', - relatedProjectsSlugs: [previousSlug], - order: 1, - }).save(); - const result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: previousSlug, - }, - }); - - const project = result.data.data.projectBySlug; - assert.equal(Number(project.id), projectWithCampaign.id); - - assert.exists(project.campaigns); - assert.isNotEmpty(project.campaigns); - - await campaign.remove(); - }); - - it('should return projects including project future power rank', async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBoosting.clear(); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - const walletAddress = generateRandomEtheriumAddress(); - const project1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - walletAddress, - }); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - const project4 = await saveProjectDirectlyToDb(createProjectData()); // Not boosted project - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const roundNumber = project1.id * 10; - - const boosting1 = await insertSinglePowerBoosting({ - user: user1, - project: project1, - percentage: 10, - }); - const boosting2 = await insertSinglePowerBoosting({ - user: user1, - project: project2, - percentage: 20, - }); - const boosting3 = await insertSinglePowerBoosting({ - user: user1, - project: project3, - percentage: 30, - }); - const boosting4 = await insertSinglePowerBoosting({ - user: user1, - project: project4, - percentage: 40, - }); - await takePowerBoostingSnapshot(); - let [powerSnapshots] = await findPowerSnapshots(); - let snapshot = powerSnapshots[0]; - - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 200, - }); - - boosting1.percentage = 100; - boosting2.percentage = 0; - boosting3.percentage = 0; - boosting4.percentage = 0; - - await PowerBoosting.save([boosting1, boosting2, boosting3, boosting4]); - - await takePowerBoostingSnapshot(); - - [powerSnapshots] = await findPowerSnapshots(); - snapshot = powerSnapshots[1]; - // Set next round for filling future power rank - snapshot.roundNumber = roundNumber + 1; - await snapshot.save(); - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 200, - }); - - await setPowerRound(roundNumber); - - await refreshUserProjectPowerView(); - await refreshProjectPowerView(); - await refreshProjectFuturePowerView(); - - const result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: project1.slug, - }, - }); - - const project = result.data.data.projectBySlug; - assert.equal(Number(project.id), project1.id); - assert.exists(project.projectPower); - assert.isTrue(project.projectPower.totalPower > 0); - - assert.equal(project.projectPower.powerRank, 4); - assert.equal(project.projectFuturePower.powerRank, 1); - }); - - it('should return projects with null project future power rank when no snapshot is synced', async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBoosting.clear(); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - - const walletAddress = generateRandomEtheriumAddress(); - const project1 = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - walletAddress, - }); - const project2 = await saveProjectDirectlyToDb(createProjectData()); - const project3 = await saveProjectDirectlyToDb(createProjectData()); - const project4 = await saveProjectDirectlyToDb(createProjectData()); // Not boosted project - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const roundNumber = project1.id * 10; - - const boosting1 = await insertSinglePowerBoosting({ - user: user1, - project: project1, - percentage: 10, - }); - const boosting2 = await insertSinglePowerBoosting({ - user: user1, - project: project2, - percentage: 20, - }); - const boosting3 = await insertSinglePowerBoosting({ - user: user1, - project: project3, - percentage: 30, - }); - const boosting4 = await insertSinglePowerBoosting({ - user: user1, - project: project4, - percentage: 40, - }); - await takePowerBoostingSnapshot(); - let [powerSnapshots] = await findPowerSnapshots(); - let snapshot = powerSnapshots[0]; - snapshot.roundNumber = roundNumber; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 200, - }); - - boosting1.percentage = 100; - boosting2.percentage = 0; - boosting3.percentage = 0; - boosting4.percentage = 0; - - await PowerBoosting.save([boosting1, boosting2, boosting3, boosting4]); - - await takePowerBoostingSnapshot(); - - [powerSnapshots] = await findPowerSnapshots(); - snapshot = powerSnapshots[1]; - // Set next round for filling future power rank - snapshot.roundNumber = roundNumber + 1; - await snapshot.save(); - await addOrUpdatePowerSnapshotBalances({ - userId: user1.id, - powerSnapshotId: snapshot.id, - balance: 0, - }); - - await setPowerRound(roundNumber); - - await refreshUserProjectPowerView(); - await refreshProjectPowerView(); - await refreshProjectFuturePowerView(false); - - let result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: project1.slug, - }, - }); - - let project = result.data.data.projectBySlug; - assert.equal(Number(project.id), project1.id); - assert.exists(project.projectPower); - assert.equal(project.projectPower.totalPower, 20); - assert.isNull(project.projectFuturePower); - // Add sync flag - await refreshProjectFuturePowerView(true); - - result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: project1.slug, - }, - }); - project = result.data.data.projectBySlug; - assert.isNotNull(project.projectFuturePower); - assert.equal(project.projectFuturePower.totalPower, 0); - }); - - it('should not return drafted if not logged in', async () => { - const draftedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.drafted, - }); - - const result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: draftedProject.slug, - }, - }); - - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_DONT_HAVE_ACCESS_TO_VIEW_THIS_PROJECT, - ); - }); - it('should not return drafted project is user is logged in but is not owner of project', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.SECOND_USER.id); - - const draftedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.drafted, - }); - - const result = await axios.post( - graphqlUrl, - { - query: fetchProjectBySlugQuery, - variables: { - slug: draftedProject.slug, - connectedWalletUserId: SEED_DATA.FIRST_USER.id, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_DONT_HAVE_ACCESS_TO_VIEW_THIS_PROJECT, - ); - }); - it('should return drafted if logged in', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - - const draftedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.drafted, - }); - - const result = await axios.post( - graphqlUrl, - { - query: fetchProjectBySlugQuery, - variables: { - slug: draftedProject.slug, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - const project = result.data.data.projectBySlug; - assert.equal(Number(project.id), draftedProject.id); - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - - it('should not return cancelled if not logged in', async () => { - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.cancelled, - }); - - const result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: project.slug, - }, - }); - - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_DONT_HAVE_ACCESS_TO_VIEW_THIS_PROJECT, - ); - }); - it('should not return cancelled project is user is logged in but is not owner of project', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.SECOND_USER.id); - - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.cancelled, - }); - - const result = await axios.post( - graphqlUrl, - { - query: fetchProjectBySlugQuery, - variables: { - slug: project.slug, - connectedWalletUserId: SEED_DATA.FIRST_USER.id, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_DONT_HAVE_ACCESS_TO_VIEW_THIS_PROJECT, - ); - }); - it('should return cancelled if logged in', async () => { - const accessToken = await generateTestAccessToken(SEED_DATA.FIRST_USER.id); - - const cancelledProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - statusId: ProjStatus.cancelled, - }); - - const result = await axios.post( - graphqlUrl, - { - query: fetchProjectBySlugQuery, - variables: { - slug: cancelledProject.slug, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - const project = result.data.data.projectBySlug; - assert.equal(Number(project.id), cancelledProject.id); - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - - it('should return project instant power get by slug', async () => { - await PowerBoosting.clear(); - await InstantPowerBalance.clear(); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const project1 = await saveProjectDirectlyToDb(createProjectData()); - - await Promise.all( - [ - [user1, project1, 100], - [user2, project1, 100], - ].map(item => { - const [user, p, percentage] = item as [User, Project, number]; - return insertSinglePowerBoosting({ - user, - project: p, - percentage, - }); - }), - ); - - await saveOrUpdateInstantPowerBalances([ - { - userId: user1.id, - balance: 10000, - balanceAggregatorUpdatedAt: new Date(1_000_000), - }, - { - userId: user2.id, - balance: 1000, - balanceAggregatorUpdatedAt: new Date(1_000_000), - }, - ]); - - await updateInstantBoosting(); - - const result = await axios.post(graphqlUrl, { - query: fetchProjectBySlugQuery, - variables: { - slug: project1.slug, - }, - }); - - const project = result.data.data.projectBySlug; - assert.equal(project.projectInstantPower.totalPower, 11000); - }); -} - -function similarProjectsBySlugTestCases() { - it('should return related projects with the exact same categories', async () => { - const viewedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - categories: ['food2', 'food3'], - }); - const secondProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - categories: ['food2', 'food3'], - }); - - const result = await axios.post(graphqlUrl, { - query: fetchSimilarProjectsBySlugQuery, - variables: { - slug: viewedProject.slug, - }, - }); - - const projects = result.data.data.similarProjectsBySlug.projects; - const totalCount = result.data.data.similarProjectsBySlug.totalCount; - - // excludes viewed project - assert.equal(totalCount, 1); - assert.equal(projects[0].id, secondProject.id); - projects.forEach(project => { - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - }); - it('should return projects with at least one matching category, if not all matched', async () => { - const viewedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - categories: ['food4', 'food8'], - }); - const secondProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - categories: ['food5', 'food8'], - }); - - const result = await axios.post(graphqlUrl, { - query: fetchSimilarProjectsBySlugQuery, - variables: { - slug: viewedProject.slug, - take: 3, - skip: 0, - }, - }); - - const c = await Category.findOne({ where: { name: 'food8' } }); - const [, relatedCount] = await Project.createQueryBuilder('project') - .innerJoinAndSelect('project.categories', 'categories') - .where('categories.id IN (:...ids)', { ids: [c?.id] }) - .andWhere('project.id != :id', { id: viewedProject.id }) - .take(1) - .skip(0) - .getManyAndCount(); - - const projects = result.data.data.similarProjectsBySlug.projects; - const totalCount = result.data.data.similarProjectsBySlug.totalCount; - - // matched food 8 category and returns related projects - assert.equal(projects[0].id, secondProject.id); - assert.isNotEmpty(projects[0].addresses); - assert.equal(totalCount, relatedCount); - assert.equal(totalCount, 1); - projects.forEach(project => { - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - }); - it('should return projects with the same admin, if no category matches', async () => { - const viewedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - categories: ['food6'], - }); - - const result = await axios.post(graphqlUrl, { - query: fetchSimilarProjectsBySlugQuery, - variables: { - slug: viewedProject.slug, - }, - }); - - const projects = result.data.data.similarProjectsBySlug.projects; - projects.forEach(project => { - assert.isOk(project.adminUser.walletAddress); - assert.isOk(project.adminUser.firstName); - assert.isNotOk(project.adminUser.email); - }); - const totalCount = result.data.data.similarProjectsBySlug.totalCount; - - const [, relatedCount] = await Project.createQueryBuilder('project') - .innerJoinAndSelect('project.categories', 'categories') - .where('project.id != :id', { id: viewedProject?.id }) - .andWhere('project.adminUserId = :ownerId', { - ownerId: SEED_DATA.FIRST_USER.id, - }) - .andWhere( - `project.statusId = ${ProjStatus.active} AND project.reviewStatus = :reviewStatus`, - { reviewStatus: ReviewStatus.Listed }, - ) - .take(1) - .skip(0) - .getManyAndCount(); - - // since no project matched the food6 category it will return all admin projects - // all projects belong to admin '1' by default - assert.equal(totalCount, relatedCount); - - // viewed project should not be present in the result set - const currentViewedProject = projects.find( - project => project.id === viewedProject.id, - ); - assert.isUndefined(currentViewedProject); - }); - it('should not throw error if project doesnt have any category ', async () => { - const viewedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - title: String(new Date().getTime()), - slug: String(new Date().getTime()), - categories: [], - }); - - const result = await axios.post(graphqlUrl, { - query: fetchSimilarProjectsBySlugQuery, - variables: { - slug: viewedProject.slug, - }, - }); - assert.isArray(result.data.data.similarProjectsBySlug.projects); - }); -} - -function addProjectUpdateTestCases() { - it('should add project update successfuly ', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - - const result = await axios.post( - graphqlUrl, - { - query: addProjectUpdateQuery, - variables: { - projectId: project.id, - content: 'TestProjectUpdateFateme', - title: 'testProjectUpdateFateme', - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - - assert.equal( - result.data.data.addProjectUpdate.title, - 'testProjectUpdateFateme', - ); - }); - - it('should change verificationStatus to null after adding update', async () => { - const verifiedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - verificationStatus: RevokeSteps.UpForRevoking, - }); - const revokedProject = await saveProjectDirectlyToDb({ - ...createProjectData(), - verificationStatus: RevokeSteps.Revoked, - }); - const accessTokenUser1 = await generateTestAccessToken( - verifiedProject.adminUserId, - ); - - await axios.post( - graphqlUrl, - { - query: addProjectUpdateQuery, - variables: { - projectId: verifiedProject.id, - content: 'Test Project Update content', - title: 'test Project Update title', - }, - }, - { - headers: { - Authorization: `Bearer ${accessTokenUser1}`, - }, - }, - ); - await axios.post( - graphqlUrl, - { - query: addProjectUpdateQuery, - variables: { - projectId: revokedProject.id, - content: 'Test Project Update content', - title: 'test Project Update title', - }, - }, - { - headers: { - Authorization: `Bearer ${accessTokenUser1}`, - }, - }, - ); - const _verifiedProject = await Project.findOne({ - where: { id: verifiedProject.id }, - }); - const _revokedProject = await Project.findOne({ - where: { id: revokedProject.id }, - }); - assert.equal(_verifiedProject?.verificationStatus, null); - assert.equal(_revokedProject?.verificationStatus, RevokeSteps.Revoked); - }); - - it('should can not add project update because of ownerShip ', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const accessTokenUser1 = await generateTestAccessToken(user1.id); - - // Add projectUpdate with accessToken user1 - const result = await axios.post( - graphqlUrl, - { - query: addProjectUpdateQuery, - variables: { - projectId: project.id, - content: 'TestProjectUpdateFateme', - title: 'testProjectUpdateFateme', - }, - }, - { - headers: { - Authorization: `Bearer ${accessTokenUser1}`, - }, - }, - ); - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_ARE_NOT_THE_OWNER_OF_PROJECT, - ); - }); - it('should can not add project update because of not found project ', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const projectUpdateCount = await ProjectUpdate.count(); - - const result = await axios.post( - graphqlUrl, - { - query: addProjectUpdateQuery, - variables: { - projectId: Number(projectUpdateCount + 1), - content: 'TestProjectUpdateFateme2', - title: 'testProjectUpdateFateme2', - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - result.data.errors[0].message, - errorMessages.PROJECT_NOT_FOUND, - ); - }); - it('should can not add project update because of lack of authentication ', async () => { - const project = await saveProjectDirectlyToDb(createProjectData()); - const result = await axios.post(graphqlUrl, { - query: addProjectUpdateQuery, - variables: { - projectId: Number(project.id), - content: 'TestProjectUpdateFateme2', - title: 'testProjectUpdateFateme2', - }, - }); - - assert.equal( - result.data.errors[0].message, - errorMessages.AUTHENTICATION_REQUIRED, - ); - }); - it('should can not add project update because user not found ', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - await User.delete({ id: user.id }); - const projectCount = await Project.count(); - const result = await axios.post( - graphqlUrl, - { - query: addProjectUpdateQuery, - variables: { - projectId: Number(projectCount || 1), - content: 'TestProjectUpdateFateme4', - title: 'testAddProjectUpdateFateme4', - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(result.data.errors[0].message, errorMessages.USER_NOT_FOUND); - }); -} - -function editProjectUpdateTestCases() { - it('should edit project update successfully ', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const updateProject = await ProjectUpdate.create({ - userId: user.id, - projectId: project.id, - content: 'TestProjectUpdateFateme', - title: 'testEditProjectUpdateFateme', - createdAt: new Date(), - isMain: false, - }).save(); - const result = await axios.post( - graphqlUrl, - { - query: editProjectUpdateQuery, - variables: { - updateId: updateProject.id, - content: '
TestProjectUpdateAfterUpdateFateme
', - title: 'testEditProjectUpdateAfterUpdateFateme', - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal( - result.data.data.editProjectUpdate.title, - 'testEditProjectUpdateAfterUpdateFateme', - ); - assert.equal( - result.data.data.editProjectUpdate.content, - '
TestProjectUpdateAfterUpdateFateme
', - ); - assert.equal( - result.data.data.editProjectUpdate.contentSummary, - 'TestProjectUpdateAfterUpdateFateme', - ); - }); - it('should can not edit project update because of ownerShip ', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project = await saveProjectDirectlyToDb(createProjectData()); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - - const accessTokenUser1 = await generateTestAccessToken(user1.id); - - const updateProject = await ProjectUpdate.create({ - userId: user.id, - projectId: project.id, - content: 'TestProjectUpdateFateme', - title: 'testEditProjectUpdateFateme', - createdAt: new Date(), - isMain: false, - }).save(); - // Add projectUpdate with accessToken user1 - const result = await axios.post( - graphqlUrl, - { - query: editProjectUpdateQuery, - variables: { - updateId: Number(updateProject.id), - content: 'TestProjectUpdateAfterUpdateFateme', - title: 'testEditProjectAfterUpdateFateme', - }, - }, - { - headers: { - Authorization: `Bearer ${accessTokenUser1}`, - }, - }, - ); - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_ARE_NOT_THE_OWNER_OF_PROJECT, - ); - }); - it('should can not edit project update because of not found project ', async () => { - const user = await User.create({ - walletAddress: generateRandomEtheriumAddress(), - loginType: 'wallet', - firstName: 'testEditProjectUpdateFateme', - }).save(); - const accessToken = await generateTestAccessToken(user.id); - const projectUpdateCount = await ProjectUpdate.count(); - const result = await axios.post( - graphqlUrl, - { - query: editProjectUpdateQuery, - variables: { - updateId: Number(projectUpdateCount + 10), - content: 'TestProjectUpdateFateme2', - title: 'testEditProjectUpdateFateme2', - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(result.data.errors[0].message, 'Project update not found.'); - }); - it('should can not edit project update because of lack of authentication ', async () => { - const project = await saveProjectDirectlyToDb(createProjectData()); - const result = await axios.post(graphqlUrl, { - query: editProjectUpdateQuery, - variables: { - updateId: Number(project.id), - content: 'TestProjectAfterUpdateFateme2', - title: 'testEditProjectAfterUpdateFateme2', - }, - }); - - assert.equal( - result.data.errors[0].message, - errorMessages.AUTHENTICATION_REQUIRED, - ); - }); -} - -function deleteProjectUpdateTestCases() { - it('should delete project update successfully ', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const accessToken = await generateTestAccessToken(user.id); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - - const updateProject = await ProjectUpdate.create({ - userId: user.id, - projectId: project.id, - content: 'TestProjectUpdateFateme', - title: 'testDeleteProjectUpdateFateme', - createdAt: new Date(), - isMain: false, - }).save(); - - const result = await axios.post( - graphqlUrl, - { - query: deleteProjectUpdateQuery, - variables: { - updateId: updateProject.id, - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(result.data.data.deleteProjectUpdate, true); - }); - it('should can not delete project update because of ownerShip ', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project = await saveProjectDirectlyToDb({ - ...createProjectData(), - adminUserId: user.id, - }); - const accessTokenUser1 = await generateTestAccessToken(user1.id); - - const updateProject = await ProjectUpdate.create({ - userId: user.id, - projectId: project.id, - content: 'TestProjectUpdateFateme', - title: 'testDeleteProjectUpdateFateme', - createdAt: new Date(), - isMain: false, - }).save(); - // Add projectUpdate with accessToken user1 - const result = await axios.post( - graphqlUrl, - { - query: deleteProjectUpdateQuery, - variables: { - updateId: Number(updateProject.id), - }, - }, - { - headers: { - Authorization: `Bearer ${accessTokenUser1}`, - }, - }, - ); - assert.equal( - result.data.errors[0].message, - errorMessages.YOU_ARE_NOT_THE_OWNER_OF_PROJECT, - ); - }); - it('should can not delete project update because of not found project ', async () => { - const user = await User.create({ - walletAddress: generateRandomEtheriumAddress(), - loginType: 'wallet', - firstName: 'testDeleteProjectUpdateFateme', - }).save(); - const accessToken = await generateTestAccessToken(user.id); - const projectUpdateCount = await ProjectUpdate.count(); - const result = await axios.post( - graphqlUrl, - { - query: deleteProjectUpdateQuery, - variables: { - updateId: Number(projectUpdateCount + 10), - }, - }, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(result.data.errors[0].message, 'Project update not found.'); - }); - it('should can not delete project update because of lack of authentication ', async () => { - const project = await saveProjectDirectlyToDb(createProjectData()); - const result = await axios.post(graphqlUrl, { - query: deleteProjectUpdateQuery, - variables: { - updateId: Number(project.id), - content: 'TestProjectAfterUpdateFateme2', - title: 'testDeleteProjectAfterUpdateFateme2', - }, - }); - - assert.equal( - result.data.errors[0].message, - errorMessages.AUTHENTICATION_REQUIRED, - ); - }); -} diff --git a/src/resolvers/projectResolver.ts b/src/resolvers/projectResolver.ts index 3da3d5d14..916b91247 100644 --- a/src/resolvers/projectResolver.ts +++ b/src/resolvers/projectResolver.ts @@ -92,10 +92,6 @@ import { resourcePerDateReportValidator, validateWithJoiSchema, } from '../utils/validators/graphqlQueryValidators'; -import { - refreshProjectFuturePowerView, - refreshProjectPowerView, -} from '../repositories/projectPowerViewRepository'; import { ResourcePerDateRange } from './donationResolver'; import { findUserReactionsByProjectIds } from '../repositories/reactionRepository'; import { AppDataSource } from '../orm'; @@ -104,7 +100,6 @@ import { findCampaignBySlug } from '../repositories/campaignRepository'; import { Campaign } from '../entities/campaign'; import { FeaturedUpdate } from '../entities/featuredUpdate'; import { PROJECT_UPDATE_CONTENT_MAX_LENGTH } from '../constants/validators'; -import { calculateGivbackFactor } from '../services/givbackService'; import { ProjectBySlugResponse } from './types/projectResolver'; import { ChainType } from '../types/network'; import { findActiveQfRound } from '../repositories/qfRoundRepository'; @@ -218,7 +213,7 @@ class GetProjectsArgs { @Field(_type => OrderBy, { defaultValue: { - field: OrderField.GIVPower, + field: OrderField.CreationAt, direction: OrderDirection.DESC, }, }) @@ -281,6 +276,7 @@ class ImageResponse { projectImageId: number; } +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => Project) export class ProjectResolver { static addCategoryQuery( @@ -497,8 +493,6 @@ export class ProjectResolver { return query.andWhere('organization.label = :label', { label: ORGANIZATION_LABELS.ENDAOMENT, }); - case FilterField.BoostedWithGivPower: - return query.andWhere(`projectPower.totalPower > 0`); case FilterField.ActiveQfRound: return query.andWhere( `EXISTS ( @@ -665,7 +659,6 @@ export class ProjectResolver { .leftJoinAndSelect('project.status', 'status') .leftJoinAndSelect('project.addresses', 'addresses') .leftJoinAndSelect('project.organization', 'organization') - .leftJoinAndSelect('project.projectPower', 'projectPower') .innerJoin('project.adminUser', 'user') .addSelect(publicSelectionFields) .orderBy('featuredUpdate.position', 'ASC', 'NULLS LAST'); @@ -726,8 +719,7 @@ export class ProjectResolver { if ( sortingBy === SortingField.ActiveQfRoundRaisedFunds || - sortingBy === SortingField.EstimatedMatching || - sortingBy === SortingField.InstantBoosting + sortingBy === SortingField.EstimatedMatching ) { activeQfRoundId = (await findActiveQfRound())?.id; } @@ -971,24 +963,9 @@ export class ProjectResolver { 'anchor_contract_address', ); } - if (fields.projectPower) { - query = query.leftJoinAndSelect('project.projectPower', 'projectPower'); - } - if (fields.projectInstantPower) { - query = query.leftJoinAndSelect( - 'project.projectInstantPower', - 'projectInstantPower', - ); - } if (fields.qfRounds) { query = query.leftJoinAndSelect('project.qfRounds', 'qfRounds'); } - if (fields.projectFuturePower) { - query = query.leftJoinAndSelect( - 'project.projectFuturePower', - 'projectFuturePower', - ); - } if (fields.campaigns) { const campaignSlugs = (await getAllProjectsRelatedToActiveCampaigns())[ minimalProject.id @@ -1038,10 +1015,6 @@ export class ProjectResolver { (project as Project).verificationFormStatus = verificationForm?.status; } } - if (fields.givbackFactor) { - const { givbackFactor } = await calculateGivbackFactor(project!.id); - return { ...project, givbackFactor }; - } // We know that we have the project because if we reach this line means minimalProject is not null return project; } @@ -2147,10 +2120,6 @@ export class ProjectResolver { await getNotificationAdapter().projectDeactivated({ project, }); - await Promise.all([ - refreshProjectPowerView(), - refreshProjectFuturePowerView(), - ]); return true; } catch (error) { logger.error('projectResolver.deactivateProject() error', error); @@ -2186,10 +2155,6 @@ export class ProjectResolver { project, }); } - await Promise.all([ - refreshProjectPowerView(), - refreshProjectFuturePowerView(), - ]); return true; } catch (error) { logger.error('projectResolver.activateProject() error', error); diff --git a/src/resolvers/projectVerificationFormResolver.ts b/src/resolvers/projectVerificationFormResolver.ts index b5a6e170d..f8867c441 100644 --- a/src/resolvers/projectVerificationFormResolver.ts +++ b/src/resolvers/projectVerificationFormResolver.ts @@ -35,6 +35,7 @@ import { countriesList } from '../utils/utils'; import { Country } from '../entities/Country'; import { getNotificationAdapter } from '../adapters/adaptersFactory'; +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => ProjectVerificationForm) export class ProjectVerificationFormResolver { // https://github.com/Giveth/impact-graph/pull/519#issuecomment-1136845612 diff --git a/src/resolvers/qfRoundHistoryResolver.ts b/src/resolvers/qfRoundHistoryResolver.ts index cecb48aaf..e1699f7e5 100644 --- a/src/resolvers/qfRoundHistoryResolver.ts +++ b/src/resolvers/qfRoundHistoryResolver.ts @@ -2,6 +2,7 @@ import { Arg, Int, Query, Resolver } from 'type-graphql'; import { QfRoundHistory } from '../entities/qfRoundHistory'; import { getQfRoundHistory } from '../repositories/qfRoundHistoryRepository'; +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => QfRoundHistory) export class QfRoundHistoryResolver { @Query(() => QfRoundHistory, { nullable: true }) diff --git a/src/resolvers/qfRoundResolver.ts b/src/resolvers/qfRoundResolver.ts index dfb931a44..0c7e993ed 100644 --- a/src/resolvers/qfRoundResolver.ts +++ b/src/resolvers/qfRoundResolver.ts @@ -98,6 +98,7 @@ export class QfRoundsArgs { activeOnly?: boolean; } +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => User) export class QfRoundResolver { @Query(_returns => [QfRound], { nullable: true }) diff --git a/src/resolvers/reactionResolver.ts b/src/resolvers/reactionResolver.ts index ac5faa82f..65271b4c3 100644 --- a/src/resolvers/reactionResolver.ts +++ b/src/resolvers/reactionResolver.ts @@ -9,6 +9,7 @@ import { getNotificationAdapter } from '../adapters/adaptersFactory'; import { findProjectById } from '../repositories/projectRepository'; import { AppDataSource } from '../orm'; +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => Reaction) export class ReactionResolver { @Query(_returns => [Reaction]) diff --git a/src/resolvers/recurringDonationResolver.ts b/src/resolvers/recurringDonationResolver.ts index 6d1c5719b..d7f18749b 100644 --- a/src/resolvers/recurringDonationResolver.ts +++ b/src/resolvers/recurringDonationResolver.ts @@ -178,6 +178,7 @@ class UserRecurringDonations { totalCount: number; } +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => AnchorContractAddress) export class RecurringDonationResolver { @Mutation(_returns => RecurringDonation, { nullable: true }) diff --git a/src/resolvers/resolvers.ts b/src/resolvers/resolvers.ts index 1ed88468a..4f35de03a 100644 --- a/src/resolvers/resolvers.ts +++ b/src/resolvers/resolvers.ts @@ -8,15 +8,10 @@ import { ReactionResolver } from './reactionResolver'; import { StatusReasonResolver } from './statusReasonResolver'; import { ProjectVerificationFormResolver } from './projectVerificationFormResolver'; import { SocialProfilesResolver } from './socialProfilesResolver'; -import { PowerBoostingResolver } from './powerBoostingResolver'; -import { UserProjectPowerResolver } from './userProjectPowerResolver'; -import { GivPowerTestingResolver } from './givPowerTestingResolver'; -import { ProjectPowerResolver } from './projectPowerResolver'; import { CampaignResolver } from './campaignResolver'; import { ChainvineResolver } from './chainvineResolver'; import { QfRoundResolver } from './qfRoundResolver'; import { QfRoundHistoryResolver } from './qfRoundHistoryResolver'; -import { ProjectUserInstantPowerViewResolver } from './instantPowerResolver'; import { AnchorContractAddressResolver } from './anchorContractAddressResolver'; import { RecurringDonationResolver } from './recurringDonationResolver'; import { DraftDonationResolver } from './draftDonationResolver'; @@ -38,11 +33,6 @@ export const getResolvers = (): Function[] => { ReactionResolver, ProjectVerificationFormResolver, SocialProfilesResolver, - PowerBoostingResolver, - UserProjectPowerResolver, - ProjectPowerResolver, - GivPowerTestingResolver, - ProjectUserInstantPowerViewResolver, CampaignResolver, QfRoundResolver, diff --git a/src/resolvers/socialProfilesResolver.ts b/src/resolvers/socialProfilesResolver.ts index 38e0eb9cf..158ceea60 100644 --- a/src/resolvers/socialProfilesResolver.ts +++ b/src/resolvers/socialProfilesResolver.ts @@ -11,6 +11,7 @@ import { getSocialNetworkAdapter } from '../adapters/adaptersFactory'; import { PROJECT_VERIFICATION_STATUSES } from '../entities/projectVerificationForm'; import { setOauth2SocialProfileInRedis } from '../services/socialProfileService'; +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => SocialProfile) export class SocialProfilesResolver { @Mutation(_returns => String) diff --git a/src/resolvers/statusReasonResolver.ts b/src/resolvers/statusReasonResolver.ts index 596fb88cd..ede1999ef 100644 --- a/src/resolvers/statusReasonResolver.ts +++ b/src/resolvers/statusReasonResolver.ts @@ -6,6 +6,7 @@ import { } from '../repositories/statusReasonRepository'; import { logger } from '../utils/logger'; +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => ProjectStatusReason) export class StatusReasonResolver { @Query(_returns => [ProjectStatusReason]) diff --git a/src/resolvers/userProjectPowerResolver.test.ts b/src/resolvers/userProjectPowerResolver.test.ts deleted file mode 100644 index 5f4f7c403..000000000 --- a/src/resolvers/userProjectPowerResolver.test.ts +++ /dev/null @@ -1,424 +0,0 @@ -import axios from 'axios'; -import { assert } from 'chai'; -import { - createProjectData, - generateRandomEtheriumAddress, - graphqlUrl, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, -} from '../../test/testUtils'; -import { getUserProjectPowerQuery } from '../../test/graphqlQueries'; -import { errorMessages } from '../utils/errorMessages'; -import { setPowerRound } from '../repositories/powerRoundRepository'; -import { refreshUserProjectPowerView } from '../repositories/userProjectPowerViewRepository'; -import { - insertSinglePowerBoosting, - takePowerBoostingSnapshot, -} from '../repositories/powerBoostingRepository'; -import { PowerBalanceSnapshot } from '../entities/powerBalanceSnapshot'; -import { PowerBoostingSnapshot } from '../entities/powerBoostingSnapshot'; -import { AppDataSource } from '../orm'; -import { addOrUpdatePowerSnapshotBalances } from '../repositories/powerBalanceSnapshotRepository'; -import { findPowerSnapshots } from '../repositories/powerSnapshotRepository'; - -describe('userProjectPowers test cases', userProjectPowersTestCases); - -function userProjectPowersTestCases() { - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - it('should get error when the user doesnt send nether projectId nor userId', async () => { - const result = await axios.post(graphqlUrl, { - query: getUserProjectPowerQuery, - variables: {}, - }); - - assert.isOk(result); - assert.equal( - result.data.errors[0].message, - errorMessages.SHOULD_SEND_AT_LEAST_ONE_OF_PROJECT_ID_AND_USER_ID, - ); - }); - it('should get list of userProjectPowers filter by userId', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - - const givbackRound = secondProject.id * 10; - - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = givbackRound; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: firstUser.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }, - { - userId: secondUser.id, - powerSnapshotId: snapshot.id, - balance: 20000, - }, - ]); - - await setPowerRound(givbackRound); - await refreshUserProjectPowerView(); - - const result = await axios.post(graphqlUrl, { - query: getUserProjectPowerQuery, - variables: { - userId: firstUser.id, - }, - }); - assert.isOk(result); - assert.equal( - result.data.data.userProjectPowers.userProjectPowers.length, - 2, - ); - result.data.data.userProjectPowers.userProjectPowers.forEach( - userProjectPower => { - assert.equal(userProjectPower.userId, firstUser.id); - }, - ); - }); - it('should have name of users within the response', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - const givbackRound = secondProject.id * 10; - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = givbackRound; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: firstUser.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }, - { - userId: secondUser.id, - powerSnapshotId: snapshot.id, - balance: 20000, - }, - ]); - - await setPowerRound(givbackRound); - await refreshUserProjectPowerView(); - - const result = await axios.post(graphqlUrl, { - query: getUserProjectPowerQuery, - variables: { - userId: firstUser.id, - }, - }); - assert.isOk(result); - assert.equal( - result.data.data.userProjectPowers.userProjectPowers.length, - 2, - ); - result.data.data.userProjectPowers.userProjectPowers.forEach( - userProjectPower => { - assert.equal(userProjectPower.userId, firstUser.id); - assert.exists(userProjectPower.user.firstName); - }, - ); - }); - it('should get list of userProjectPowers filter by projectId', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const thirdUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: thirdUser, - project: firstProject, - percentage: 13, - }); - await insertSinglePowerBoosting({ - user: thirdUser, - project: secondProject, - percentage: 70, - }); - - const givbackRound = secondProject.id * 10; - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = givbackRound; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: firstUser.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }, - { - userId: secondUser.id, - powerSnapshotId: snapshot.id, - balance: 20000, - }, - { - userId: thirdUser.id, - powerSnapshotId: snapshot.id, - balance: 30000, - }, - ]); - - await setPowerRound(givbackRound); - await refreshUserProjectPowerView(); - const result = await axios.post(graphqlUrl, { - query: getUserProjectPowerQuery, - variables: { - projectId: firstProject.id, - }, - }); - assert.isOk(result); - assert.equal( - result.data.data.userProjectPowers.userProjectPowers.length, - 3, - ); - let lastBoostedPower = - result.data.data.userProjectPowers.userProjectPowers[0].boostedPower; - result.data.data.userProjectPowers.userProjectPowers.forEach( - (userProjectPower, index) => { - assert.equal(userProjectPower.projectId, firstProject.id); - assert.equal(userProjectPower.rank, index + 1); - assert.isTrue(userProjectPower.boostedPower <= lastBoostedPower); - lastBoostedPower = userProjectPower.boostedPower; - }, - ); - }); - - it('should get list of userProjectPowers filter by projectId and userId', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 3, - }); - - const givbackRound = secondProject.id * 10; - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = givbackRound; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: firstUser.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }, - { - userId: secondUser.id, - powerSnapshotId: snapshot.id, - balance: 20000, - }, - ]); - - await setPowerRound(givbackRound); - await refreshUserProjectPowerView(); - const result = await axios.post(graphqlUrl, { - query: getUserProjectPowerQuery, - variables: { - projectId: firstProject.id, - userId: firstUser.id, - }, - }); - assert.isOk(result); - assert.equal( - result.data.data.userProjectPowers.userProjectPowers.length, - 1, - ); - - result.data.data.userProjectPowers.userProjectPowers.forEach( - userProjectPower => { - assert.equal(userProjectPower.projectId, firstProject.id); - assert.equal(userProjectPower.userId, firstUser.id); - }, - ); - }); - it('should get list of userProjectPowers filter by projectId and userId, should not send user email in response', async () => { - const firstUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const secondUser = await saveUserDirectlyToDb( - generateRandomEtheriumAddress(), - ); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - await insertSinglePowerBoosting({ - user: firstUser, - project: secondProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: firstProject, - percentage: 3, - }); - await insertSinglePowerBoosting({ - user: secondUser, - project: secondProject, - percentage: 3, - }); - - const givbackRound = secondProject.id * 10; - - await takePowerBoostingSnapshot(); - const [powerSnapshots] = await findPowerSnapshots(); - const snapshot = powerSnapshots[0]; - - snapshot.blockNumber = 1; - snapshot.roundNumber = givbackRound; - await snapshot.save(); - - await addOrUpdatePowerSnapshotBalances([ - { - userId: firstUser.id, - powerSnapshotId: snapshot.id, - balance: 10000, - }, - { - userId: secondUser.id, - powerSnapshotId: snapshot.id, - balance: 20000, - }, - ]); - - await setPowerRound(givbackRound); - await refreshUserProjectPowerView(); - - const result = await axios.post(graphqlUrl, { - query: getUserProjectPowerQuery, - variables: { - projectId: firstProject.id, - }, - }); - assert.isOk(result); - result.data.data.userProjectPowers.userProjectPowers.forEach( - userProjectPower => { - assert.isNotOk(userProjectPower.user.email); - }, - ); - }); -} diff --git a/src/resolvers/userProjectPowerResolver.ts b/src/resolvers/userProjectPowerResolver.ts deleted file mode 100644 index 9d66d426f..000000000 --- a/src/resolvers/userProjectPowerResolver.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { - Args, - ArgsType, - Field, - InputType, - Int, - ObjectType, - Query, - registerEnumType, - Resolver, -} from 'type-graphql'; -import { Max, Min } from 'class-validator'; -import { Service } from 'typedi'; -import { i18n, translationErrorMessagesKeys } from '../utils/errorMessages'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { UserProjectPowerView } from '../views/userProjectPowerView'; -import { getUserProjectPowers } from '../repositories/userProjectPowerViewRepository'; - -export enum UserPowerOrderDirection { - ASC = 'ASC', - DESC = 'DESC', -} - -export enum UserPowerOrderField { - Percentage = 'percentage', - BoostedPower = 'boostedPower', -} - -registerEnumType(UserPowerOrderField, { - name: 'UserPowerOrderField', - description: 'Order by field', -}); - -registerEnumType(UserPowerOrderDirection, { - name: 'UserPowerOrderDirection', - description: 'Order direction', -}); - -@InputType() -export class UserPowerOrderBy { - @Field(_type => UserPowerOrderField, { - nullable: true, - defaultValue: UserPowerOrderField.BoostedPower, - }) - field: UserPowerOrderField | null; - - @Field(_type => UserPowerOrderDirection, { - nullable: true, - defaultValue: UserPowerOrderDirection.DESC, - }) - direction: UserPowerOrderDirection; -} - -@Service() -@ArgsType() -export class UserProjectPowerArgs { - @Field(_type => Int, { defaultValue: 0 }) - @Min(0) - skip: number; - - @Field(_type => Int, { defaultValue: 20 }) - @Min(0) - @Max(50) - take: number; - - @Field(_type => UserPowerOrderBy, { - nullable: true, - defaultValue: { - field: UserPowerOrderField.BoostedPower, - direction: UserPowerOrderDirection.DESC, - }, - }) - orderBy: UserPowerOrderBy; - - @Field(_type => Int, { nullable: true }) - projectId?: number; - - @Field(_type => Int, { nullable: true }) - userId?: number; - - @Field(_type => Int, { nullable: true }) - round?: number; -} - -@ObjectType() -class UserProjectPowers { - @Field(_type => [UserProjectPowerView]) - userProjectPowers: UserProjectPowerView[]; - - @Field(_type => Int) - totalCount: number; -} - -@Resolver(_of => PowerBoosting) -export class UserProjectPowerResolver { - @Query(_returns => UserProjectPowers) - async userProjectPowers( - @Args() - { take, skip, projectId, userId, orderBy }: UserProjectPowerArgs, - ): Promise { - if (!projectId && !userId) { - throw new Error( - i18n.__( - translationErrorMessagesKeys.SHOULD_SEND_AT_LEAST_ONE_OF_PROJECT_ID_AND_USER_ID, - ), - ); - } - const [userProjectPowers, totalCount] = await getUserProjectPowers({ - userId, - projectId, - skip, - orderBy, - take, - }); - return { - userProjectPowers, - totalCount, - }; - } -} diff --git a/src/resolvers/userResolver.test.ts b/src/resolvers/userResolver.test.ts index d996747f9..fff358e1e 100644 --- a/src/resolvers/userResolver.test.ts +++ b/src/resolvers/userResolver.test.ts @@ -22,7 +22,6 @@ import { userByAddress, } from '../../test/graphqlQueries'; import { errorMessages } from '../utils/errorMessages'; -import { insertSinglePowerBoosting } from '../repositories/powerBoostingRepository'; import { DONATION_STATUS } from '../entities/donation'; import { getGitcoinAdapter } from '../adapters/adaptersFactory'; import { updateUserTotalDonated } from '../services/userService'; @@ -451,44 +450,6 @@ function userByAddressTestCases() { assert.equal(result.data.data.userByAddress.donationsCount, 2); }); - // TODO write test cases for likedProjectsCount, donationsCount, projectsCount fields - it('Return boostedProjectsCount of a user', async () => { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const secondProject = await saveProjectDirectlyToDb(createProjectData()); - const thirdProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user, - project: firstProject, - percentage: 10, - }); - await insertSinglePowerBoosting({ - user, - project: secondProject, - percentage: 15, - }); - await insertSinglePowerBoosting({ - user, - project: thirdProject, - percentage: 20, - }); - const accessToken = await generateTestAccessToken(user.id); - const result = await axios.post( - graphqlUrl, - { - query: userByAddress, - variables: { - address: user.walletAddress, - }, - }, - { - headers: { - authorization: `Bearer ${accessToken}`, - }, - }, - ); - assert.equal(result.data.data.userByAddress.boostedProjectsCount, 3); - }); it('Returns null when no user is found', async () => { const result = await axios.post(graphqlUrl, { query: userByAddress, diff --git a/src/resolvers/userResolver.ts b/src/resolvers/userResolver.ts index 487f9ac28..4a1861cfb 100644 --- a/src/resolvers/userResolver.ts +++ b/src/resolvers/userResolver.ts @@ -40,6 +40,7 @@ class UserRelatedAddressResponse { hasDonated: boolean; } +// eslint-disable-next-line unused-imports/no-unused-imports @Resolver(_of => User) export class UserResolver { constructor(private readonly userRepository: Repository) { diff --git a/src/server/adminJs/tabs/donationTab.test.ts b/src/server/adminJs/tabs/donationTab.test.ts index 7d1980822..e94a4e98c 100644 --- a/src/server/adminJs/tabs/donationTab.test.ts +++ b/src/server/adminJs/tabs/donationTab.test.ts @@ -63,7 +63,7 @@ function updateDonationPriceTestCases() { } function createDonationTestCases() { - it('Should create donations for csv airDrop', async () => { + it.skip('Should create donations for csv airDrop', async () => { // https://blockscout.com/xdai/mainnet/tx/0x7a063fbb9dc674f814b8b7607e64f20e09ce4b891de72360d8be3e5ac92a4351 const ethPrice = 2800; diff --git a/src/server/adminJs/tabs/donationTab.ts b/src/server/adminJs/tabs/donationTab.ts index a2eb3779d..b4a3364a9 100644 --- a/src/server/adminJs/tabs/donationTab.ts +++ b/src/server/adminJs/tabs/donationTab.ts @@ -29,7 +29,6 @@ import { translationErrorMessagesKeys, } from '../../../utils/errorMessages'; import { Project } from '../../../entities/project'; -import { calculateGivbackFactor } from '../../../services/givbackService'; import { findUserByWalletAddress } from '../../../repositories/userRepository'; import { updateUserTotalDonated, @@ -126,13 +125,7 @@ export const createDonation = async ( continue; } - const { givbackFactor, projectRank, bottomRankInRound, powerRound } = - await calculateGivbackFactor(project.id); const donation = Donation.create({ - givbackFactor, - projectRank, - bottomRankInRound, - powerRound, fromWalletAddress: transactionInfo?.from, toWalletAddress: transactionInfo?.to, transactionId: txHash, @@ -502,18 +495,6 @@ export const donationTab = { new: false, }, }, - givbackFactor: { - isVisible: false, - }, - projectRank: { - isVisible: false, - }, - bottomRankInRound: { - isVisible: false, - }, - powerRound: { - isVisible: false, - }, referrerWallet: { isVisible: { list: false, diff --git a/src/server/adminJs/tabs/projectsTab.ts b/src/server/adminJs/tabs/projectsTab.ts index d0cf6fbe2..df8aa470e 100644 --- a/src/server/adminJs/tabs/projectsTab.ts +++ b/src/server/adminJs/tabs/projectsTab.ts @@ -22,12 +22,6 @@ import { import { NOTIFICATIONS_EVENT_NAMES } from '../../../analytics/analytics'; import { HISTORY_DESCRIPTIONS } from '../../../entities/projectStatusHistory'; import { getNotificationAdapter } from '../../../adapters/adaptersFactory'; -import { changeUserBoostingsAfterProjectCancelled } from '../../../services/powerBoostingService'; -import { refreshUserProjectPowerView } from '../../../repositories/userProjectPowerViewRepository'; -import { - refreshProjectFuturePowerView, - refreshProjectPowerView, -} from '../../../repositories/projectPowerViewRepository'; import { logger } from '../../../utils/logger'; import { findSocialProfilesByProjectId } from '../../../repositories/socialProfileRepository'; import { findProjectUpdatesByProjectId } from '../../../repositories/projectUpdateRepository'; @@ -268,12 +262,6 @@ export const verifyProjects = async ( } } } - - await Promise.all([ - refreshUserProjectPowerView(), - refreshProjectPowerView(), - refreshProjectFuturePowerView(), - ]); } catch (error) { logger.error('verifyProjects() error', error); throw error; @@ -342,9 +330,6 @@ export const updateStatusOfProjects = async ( await getNotificationAdapter().projectCancelled({ project: projectWithAdmin, }); - await changeUserBoostingsAfterProjectCancelled({ - projectId: project.id, - }); } else if (status === ProjStatus.active) { await getNotificationAdapter().projectReactivated({ project: projectWithAdmin, @@ -355,11 +340,6 @@ export const updateStatusOfProjects = async ( }); } } - await Promise.all([ - refreshUserProjectPowerView(), - refreshProjectFuturePowerView(), - refreshProjectPowerView(), - ]); } return { redirectUrl: '/admin/resources/Project', @@ -1140,22 +1120,7 @@ export const projectsTab = { project.listed = null; await project.save(); } - - if ( - statusChanges?.includes( - NOTIFICATIONS_EVENT_NAMES.PROJECT_CANCELLED, - ) - ) { - await changeUserBoostingsAfterProjectCancelled({ - projectId: project.id, - }); - } } - await Promise.all([ - refreshUserProjectPowerView(), - refreshProjectFuturePowerView(), - refreshProjectPowerView(), - ]); return request; }, }, diff --git a/src/server/bootstrap.ts b/src/server/bootstrap.ts index ae5458ef1..10e20495b 100644 --- a/src/server/bootstrap.ts +++ b/src/server/bootstrap.ts @@ -43,19 +43,12 @@ import { oauth2CallbacksRouter, SOCIAL_PROFILES_PREFIX, } from '../routers/oauth2Callbacks'; -import { - dropDbCronExtension, - schedulePowerBoostingSnapshot, - schedulePowerSnapshotsHistory, -} from '../repositories/dbCronRepository'; -import { runFillPowerSnapshotBalanceCronJob } from '../services/cronJobs/fillSnapshotBalances'; -import { runUpdatePowerRoundCronJob } from '../services/cronJobs/updatePowerRoundJob'; +import { dropDbCronExtension } from '../repositories/dbCronRepository'; import { onramperWebhookHandler } from '../services/onramper/webhookHandler'; import { AppDataSource, CronDataSource } from '../orm'; import { ApolloContext } from '../types/ApolloContext'; import { ProjectResolverWorker } from '../workers/projectsResolverWorker'; -import { runInstantBoostingUpdateCronJob } from '../services/cronJobs/instantBoostingUpdateJob'; import { refreshProjectEstimatedMatchingView } from '../services/projectViewsService'; import { isTestEnv } from '../utils/utils'; import { runCheckActiveStatusOfQfRounds } from '../services/cronJobs/checkActiveStatusQfRounds'; @@ -313,26 +306,6 @@ export async function bootstrap() { async function continueDbSetup() { logger.debug('continueDbSetup() has been called', new Date()); - - const enableDbCronJob = - config.get('ENABLE_DB_POWER_BOOSTING_SNAPSHOT') === 'true'; - if (enableDbCronJob) { - try { - const scheduleExpression = config.get( - 'DB_POWER_BOOSTING_SNAPSHOT_CRONJOB_EXPRESSION', - ) as string; - const powerSnapshotsHistoricScheduleExpression = config.get( - 'ARCHIVE_POWER_BOOSTING_OLD_SNAPSHOT_DATA_CRONJOB_EXPRESSION', - ) as string; - await schedulePowerBoostingSnapshot(scheduleExpression); - await schedulePowerSnapshotsHistory( - powerSnapshotsHistoricScheduleExpression, - ); - } catch (e) { - logger.error('Enabling power boosting snapshot ', e); - } - } - if (!isTestEnv) { // They will fail in test env, because we run migrations after bootstrap so refreshing them will cause this error // relation "project_estimated_matching_view" does not exist @@ -382,29 +355,6 @@ export async function bootstrap() { // runDraftRecurringDonationMatchWorkerJob(); } - if (process.env.FILL_POWER_SNAPSHOT_BALANCE_SERVICE_ACTIVE === 'true') { - runFillPowerSnapshotBalanceCronJob(); - } - logger.debug('Running givPower cron jobs info ', { - UPDATE_POWER_SNAPSHOT_SERVICE_ACTIVE: config.get( - 'UPDATE_POWER_SNAPSHOT_SERVICE_ACTIVE', - ), - ENABLE_INSTANT_BOOSTING_UPDATE: config.get( - 'ENABLE_INSTANT_BOOSTING_UPDATE', - ), - INSTANT_BOOSTING_UPDATE_CRONJOB_EXPRESSION: config.get( - 'INSTANT_BOOSTING_UPDATE_CRONJOB_EXPRESSION', - ), - UPDATE_POWER_ROUND_CRONJOB_EXPRESSION: config.get( - 'UPDATE_POWER_ROUND_CRONJOB_EXPRESSION', - ), - }); - if (process.env.UPDATE_POWER_SNAPSHOT_SERVICE_ACTIVE === 'true') { - runUpdatePowerRoundCronJob(); - } - if (process.env.ENABLE_INSTANT_BOOSTING_UPDATE === 'true') { - runInstantBoostingUpdateCronJob(); - } if (process.env.ENABLE_UPDATE_RECURRING_DONATION_STREAM === 'true') { runUpdateRecurringDonationStream(); runCheckUserSuperTokenBalancesJob(); diff --git a/src/services/Idriss/contractDonations.ts b/src/services/Idriss/contractDonations.ts index feb540782..1120cc5ad 100644 --- a/src/services/Idriss/contractDonations.ts +++ b/src/services/Idriss/contractDonations.ts @@ -20,7 +20,6 @@ import { } from '../../repositories/userRepository'; import { logger } from '../../utils/logger'; import { getGitcoinAdapter } from '../../adapters/adaptersFactory'; -import { calculateGivbackFactor } from '../givbackService'; import { updateUserTotalDonated, updateUserTotalReceived, @@ -253,13 +252,6 @@ export const createIdrissTwitterDonation = async ( donation.valueEth = Number(idrissDonation.amount) * donation.priceEth; } - const { givbackFactor, projectRank, bottomRankInRound, powerRound } = - await calculateGivbackFactor(project.id); - donation.givbackFactor = givbackFactor; - donation.projectRank = projectRank; - donation.bottomRankInRound = bottomRankInRound; - donation.powerRound = powerRound; - await donation.save(); await updateUserTotalDonated(donation.userId); diff --git a/src/services/chains/index.test.ts b/src/services/chains/index.test.ts index e7145b44c..ea51729cc 100644 --- a/src/services/chains/index.test.ts +++ b/src/services/chains/index.test.ts @@ -8,8 +8,8 @@ import { closeTo, getTransactionInfoFromNetwork } from './index'; const ONE_DAY = 60 * 60 * 24; -describe('getTransactionDetail test cases', getTransactionDetailTestCases); -describe('closeTo test cases', closeToTestCases); +describe.skip('getTransactionDetail test cases', getTransactionDetailTestCases); +describe.skip('closeTo test cases', closeToTestCases); function getTransactionDetailTestCases() { // it('should return transaction detail for normal transfer on gnosis when it belongs to a multisig', async () => { diff --git a/src/services/cronJobs/fillSnapshotBalances.test.ts b/src/services/cronJobs/fillSnapshotBalances.test.ts deleted file mode 100644 index af8ee9915..000000000 --- a/src/services/cronJobs/fillSnapshotBalances.test.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { assert } from 'chai'; -import sinon from 'sinon'; -import { - addFillPowerSnapshotBalanceJobsToQueue, - processFillPowerSnapshotJobs, -} from './fillSnapshotBalances'; -import { getPowerBoostingSnapshotWithoutBalance } from '../../repositories/powerSnapshotRepository'; -import { - createProjectData, - generateRandomEtheriumAddress, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, - sleep, -} from '../../../test/testUtils'; -import { PowerSnapshot } from '../../entities/powerSnapshot'; -import { PowerBoostingSnapshot } from '../../entities/powerBoostingSnapshot'; -import { AppDataSource } from '../../orm'; -import { PowerBalanceSnapshot } from '../../entities/powerBalanceSnapshot'; -import { getPowerBalanceAggregatorAdapter } from '../../adapters/adaptersFactory'; -import { convertTimeStampToSeconds } from '../../utils/utils'; - -describe( - 'processFillPowerSnapshotJobs test cases', - processFillPowerSnapshotJobsTestCases, -); - -async function processFillPowerSnapshotJobsTestCases() { - let stub: sinon.SinonStub; - - beforeEach(async () => { - await AppDataSource.getDataSource().query( - 'truncate power_snapshot cascade', - ); - await PowerBalanceSnapshot.clear(); - await PowerBoostingSnapshot.clear(); - }); - - afterEach(() => { - stub?.restore(); - }); - - before(async () => { - await processFillPowerSnapshotJobs(); - }); - - it('should fill snapShotBalances for powerSnapshots', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - const powerSnapshotTime = new Date().getTime() - 1 * 3600 * 1000; // 1 hour earlier - - const powerSnapshots = PowerSnapshot.create([ - { - time: new Date(powerSnapshotTime), - }, - { - time: new Date(powerSnapshotTime + 1000), - }, - ]); - await PowerSnapshot.save(powerSnapshots); - - const powerBoostingSnapshots = PowerBoostingSnapshot.create([ - { - userId: user1.id, - projectId: project1.id, - percentage: 10, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user2.id, - projectId: project1.id, - percentage: 20, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user1.id, - projectId: project1.id, - percentage: 11, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user2.id, - projectId: project1.id, - percentage: 21, - powerSnapshot: powerSnapshots[1], - }, - ]); - - const powerBalances = PowerBalanceSnapshot.create([ - { - userId: user1.id, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user1.id, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[1], - }, - ]); - await PowerBalanceSnapshot.save(powerBalances); - await PowerBoostingSnapshot.save(powerBoostingSnapshots); - assert.isNotEmpty(await getPowerBoostingSnapshotWithoutBalance()); - await addFillPowerSnapshotBalanceJobsToQueue(); - - // Give time to process jobs - await sleep(5_000); - assert.equal((await getPowerBoostingSnapshotWithoutBalance()).length, 0); - }); - - it('should not fill snapShotBalances when balance aggregator is not updated after powerSnapshot', async () => { - const powerSnapshotTimeBeforeSyncTime = - new Date().getTime() - 2 * 3600 * 1000; // 2 hour earlier - const powerSnapshotTime = new Date().getTime() - 1 * 3600 * 1000; // 1 hour earlier - - stub = sinon - .stub(getPowerBalanceAggregatorAdapter(), 'getLeastIndexedBlockTimeStamp') - // I decreased powerSnapshotTime 5 seconds to make sure those snapshot would not fill balances - .resolves(convertTimeStampToSeconds(powerSnapshotTime) - 5); - - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project1 = await saveProjectDirectlyToDb(createProjectData()); - - const powerSnapshots = PowerSnapshot.create([ - { - time: new Date(powerSnapshotTime), - }, - { - time: new Date(powerSnapshotTime + 1000), - }, - { - time: new Date(powerSnapshotTimeBeforeSyncTime), - }, - ]); - await PowerSnapshot.save(powerSnapshots); - - const powerBoostingSnapshots = PowerBoostingSnapshot.create([ - { - userId: user1.id, - projectId: project1.id, - percentage: 10, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user2.id, - projectId: project1.id, - percentage: 20, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user1.id, - projectId: project1.id, - percentage: 11, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user2.id, - projectId: project1.id, - percentage: 21, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user1.id, - projectId: project1.id, - percentage: 12, - powerSnapshot: powerSnapshots[2], - }, - { - userId: user2.id, - projectId: project1.id, - percentage: 32, - powerSnapshot: powerSnapshots[2], - }, - ]); - - const powerBalances = PowerBalanceSnapshot.create([ - { - userId: user1.id, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user1.id, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user1.id, - powerSnapshot: powerSnapshots[2], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[2], - }, - ]); - await PowerBalanceSnapshot.save(powerBalances); - await PowerBoostingSnapshot.save(powerBoostingSnapshots); - assert.isNotEmpty(await getPowerBoostingSnapshotWithoutBalance()); - await addFillPowerSnapshotBalanceJobsToQueue(); - - // Give time to process jobs - await sleep(5_000); - const powerBoostingWithoutBalances = - await getPowerBoostingSnapshotWithoutBalance(); - assert.equal(powerBoostingWithoutBalances.length, 4); - powerBoostingWithoutBalances.forEach(pb => { - // powerSnapshots[2] time is before synced time of balance aggregator, so it must have been filled - assert.notEqual(pb.powerSnapshotId, powerSnapshots[2].id); - }); - }); - - it('should fill snapshot balances when we have snapshots taken in same seconds', async () => { - const powerSnapshotTime = new Date().getTime() - 1 * 3600 * 1000; // 1 hour earlier - - for (let i = 0; i < 110; i++) { - const user = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const project = await saveProjectDirectlyToDb(createProjectData()); - const powerSnapshots = PowerSnapshot.create([ - { - time: new Date(powerSnapshotTime + (i + 1) * 1000), - }, - { - time: new Date(powerSnapshotTime + 500 + (i + 1) * 1000), - }, - ]); - await PowerSnapshot.save(powerSnapshots); - - const powerBoostingSnapshots = PowerBoostingSnapshot.create([ - { - userId: user.id, - projectId: project.id, - percentage: 10, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user2.id, - projectId: project.id, - percentage: 20, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user.id, - projectId: project.id, - percentage: 30, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user2.id, - projectId: project.id, - percentage: 40, - powerSnapshot: powerSnapshots[1], - }, - ]); - await PowerBoostingSnapshot.save(powerBoostingSnapshots); - - const powerBalances = PowerBalanceSnapshot.create([ - { - userId: user.id, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[0], - }, - { - userId: user.id, - powerSnapshot: powerSnapshots[1], - }, - { - userId: user2.id, - powerSnapshot: powerSnapshots[1], - }, - ]); - await PowerBalanceSnapshot.save(powerBalances); - } - - assert.isNotEmpty(await getPowerBoostingSnapshotWithoutBalance()); - - await addFillPowerSnapshotBalanceJobsToQueue(); - - await sleep(4_000); - - assert.equal((await getPowerBoostingSnapshotWithoutBalance()).length, 0); - // assert.isEmpty(await getPowerBoostingSnapshotWithoutBalance()); - }); -} diff --git a/src/services/cronJobs/fillSnapshotBalances.ts b/src/services/cronJobs/fillSnapshotBalances.ts deleted file mode 100644 index 97e3ef39c..000000000 --- a/src/services/cronJobs/fillSnapshotBalances.ts +++ /dev/null @@ -1,161 +0,0 @@ -import Bull from 'bull'; -import { schedule } from 'node-cron'; -import _ from 'lodash'; -import { redisConfig } from '../../redis'; -import { logger } from '../../utils/logger'; -import config from '../../config'; -import { getPowerBalanceAggregatorAdapter } from '../../adapters/adaptersFactory'; -import { - getPowerBoostingSnapshotWithoutBalance, - GetPowerBoostingSnapshotWithoutBalanceOutput, -} from '../../repositories/powerSnapshotRepository'; -import { addOrUpdatePowerSnapshotBalances } from '../../repositories/powerBalanceSnapshotRepository'; - -// Constants -const FILL_SNAPSHOT_BALANCE_QUEUE_NAME = 'fill-snapshot-balance-aggregator'; -const TWO_MINUTES = 1000 * 60 * 2; -const DEFAULT_CRON_JOB_TIME = '0 0 * * * *'; -const DEFAULT_CONCURRENT_JOB_COUNT = 1; - -// Queue for filling snapshot balances -const fillSnapshotBalanceQueue = new Bull( - FILL_SNAPSHOT_BALANCE_QUEUE_NAME, - { redis: redisConfig }, -); - -// Periodically log the queue count -setInterval(async () => { - const count = await fillSnapshotBalanceQueue.count(); - logger.debug('Fill power snapshot balance queues count:', { count }); -}, TWO_MINUTES); - -const numberOfFillPowerSnapshotBalancesConcurrentJob = Number( - config.get('NUMBER_OF_FILLING_POWER_SNAPSHOT_BALANCE_CONCURRENT_JOB') || - DEFAULT_CONCURRENT_JOB_COUNT, -); - -const cronJobTime = - (config.get('FILL_POWER_SNAPSHOT_BALANCE_CRONJOB_EXPRESSION') as string) || - DEFAULT_CRON_JOB_TIME; - -export const runFillPowerSnapshotBalanceCronJob = () => { - logger.debug( - 'runSyncUserPowersCronJob() has been called, cronJobTime', - cronJobTime, - ); - processFillPowerSnapshotJobs(); - - // Schedule cron job to add jobs to queue - schedule(cronJobTime, async () => { - await addFillPowerSnapshotBalanceJobsToQueue(); - }); -}; - -export async function addFillPowerSnapshotBalanceJobsToQueue() { - const balanceAggregatorLastUpdatedTime = - await getPowerBalanceAggregatorAdapter().getLeastIndexedBlockTimeStamp({}); - - const groupByTimestamp: Record< - number, - { - userId: number; - powerSnapshotId: number; - walletAddress: string; - }[] - > = {}; - - let offset = 0; - let powerBoostings: GetPowerBoostingSnapshotWithoutBalanceOutput[] = []; - const snapshotTimestampsAheadOfBalanceAggregator = new Set(); - do { - powerBoostings = await getPowerBoostingSnapshotWithoutBalance(100, offset); - powerBoostings.forEach(pb => { - if (pb.timestamp > balanceAggregatorLastUpdatedTime) { - snapshotTimestampsAheadOfBalanceAggregator.add(pb.timestamp); - return; - } - - const timestampInStr = String(pb.timestamp); - groupByTimestamp[timestampInStr] = groupByTimestamp[timestampInStr] || []; - groupByTimestamp[timestampInStr].push(pb); - }); - offset += powerBoostings.length; - } while (powerBoostings.length); - - // log for each item in the set - snapshotTimestampsAheadOfBalanceAggregator.forEach(timestamp => { - logger.error('The balance aggregator has not synced to snapshot point ', { - snapshotTime: new Date(timestamp * 1000), - balanceAggregatorLastUpdatedDate: new Date( - balanceAggregatorLastUpdatedTime * 1000, - ), - }); - }); - - for (const [key, value] of Object.entries(groupByTimestamp)) { - const jobData = { - timestamp: +key, - data: value.map(pb => ({ - userId: pb.userId, - powerSnapshotId: pb.powerSnapshotId, - walletAddress: pb.walletAddress.toLowerCase(), - })), - }; - fillSnapshotBalanceQueue.add(jobData, { - jobId: `${FILL_SNAPSHOT_BALANCE_QUEUE_NAME}-${key}`, - }); - } -} - -export function processFillPowerSnapshotJobs() { - fillSnapshotBalanceQueue.process( - numberOfFillPowerSnapshotBalancesConcurrentJob, - async (job, done) => { - try { - const { timestamp, data } = job.data; - const batchNumber = Number( - process.env.NUMBER_OF_BALANCE_AGGREGATOR_BATCH || 20, - ); - - // Process in batches - for (let i = 0; i < Math.ceil(data.length / batchNumber); i++) { - const batch = data.slice(i * batchNumber, (i + 1) * batchNumber); - const addresses = batch.map(item => item.walletAddress); - const balances = - await getPowerBalanceAggregatorAdapter().getAddressesBalance({ - timestamp, - addresses, - }); - - const groupByWalletAddress = _.groupBy(batch, item => - item.walletAddress.toLowerCase(), - ); - - const snapshotBalances = balances - .map(balance => - groupByWalletAddress[balance.address.toLowerCase()].map(item => ({ - balance: balance.balance, - powerSnapshotId: item.powerSnapshotId, - userId: item!.userId, - })), - ) - .flat(); - await addOrUpdatePowerSnapshotBalances(snapshotBalances); - } - } catch (e) { - logger.error('processFillPowerSnapshotJobs >> error', e); - } finally { - done(); - } - }, - ); -} - -interface FillSnapShotBalanceData { - timestamp: number; - data: { - userId: number; - powerSnapshotId: number; - walletAddress: string; - }[]; -} diff --git a/src/services/cronJobs/importLostDonationsJob.ts b/src/services/cronJobs/importLostDonationsJob.ts index 5438cc6c5..481816dac 100644 --- a/src/services/cronJobs/importLostDonationsJob.ts +++ b/src/services/cronJobs/importLostDonationsJob.ts @@ -10,7 +10,6 @@ import { erc20ABI } from '../../assets/erc20ABI'; import { User } from '../../entities/user'; import { Token } from '../../entities/token'; import { Project } from '../../entities/project'; -import { calculateGivbackFactor } from '../givbackService'; import { getUserDonationStats, updateUserTotalDonated, @@ -205,9 +204,6 @@ export const importLostDonations = async () => { ); } - const { givbackFactor, projectRank, powerRound, bottomRankInRound } = - await calculateGivbackFactor(project.id as number); - let dbDonation: Donation; try { dbDonation = Donation.create({ @@ -225,10 +221,6 @@ export const importLostDonations = async () => { ), transactionNetworkId: networkId, createdAt: donationDateDbFormat, - givbackFactor, - projectRank, - powerRound, - bottomRankInRound, status: 'verified', anonymous: false, segmentNotified: true, @@ -258,7 +250,6 @@ export const importLostDonations = async () => { totalDonated: donationStats?.totalDonated, donationsCount: donationStats?.donationsCount, lastDonationDate: donationStats?.lastDonationDate, - GIVbacksRound: dbDonation.powerRound, QFDonor: dbDonation.qfRound?.name, donationChain: NETWORKS_IDS_TO_NAME[dbDonation.transactionNetworkId], }); diff --git a/src/services/cronJobs/instantBoostingUpdateJob.ts b/src/services/cronJobs/instantBoostingUpdateJob.ts deleted file mode 100644 index 660954105..000000000 --- a/src/services/cronJobs/instantBoostingUpdateJob.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { schedule } from 'node-cron'; -import config from '../../config'; -import { logger } from '../../utils/logger'; -import { updateInstantBoosting } from '../instantBoostingServices'; - -const cronJobTime = - (config.get('INSTANT_BOOSTING_UPDATE_CRONJOB_EXPRESSION') as string) || - '0 */5 * * *'; - -export const runInstantBoostingUpdateCronJob = () => { - logger.debug( - 'runRefreshInstantBoostingRefreshCronJob() has been called, cronJobTime', - cronJobTime, - ); - schedule(cronJobTime, async () => { - await updateInstantBoosting(); - }); -}; diff --git a/src/services/cronJobs/updatePowerRoundJob.ts b/src/services/cronJobs/updatePowerRoundJob.ts deleted file mode 100644 index d36f94c5d..000000000 --- a/src/services/cronJobs/updatePowerRoundJob.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { schedule } from 'node-cron'; -import config from '../../config'; -import { logger } from '../../utils/logger'; -import { - getPowerRound, - setPowerRound, -} from '../../repositories/powerRoundRepository'; -import { getRoundNumberByDate } from '../../utils/powerBoostingUtils'; -import { - refreshProjectPowerView, - refreshProjectFuturePowerView, - getBottomRank, -} from '../../repositories/projectPowerViewRepository'; -import { refreshUserProjectPowerView } from '../../repositories/userProjectPowerViewRepository'; -import { - copyProjectRanksToPreviousRoundRankTable, - projectsThatTheirRanksHaveChanged, -} from '../../repositories/previousRoundRankRepository'; -import { getNotificationAdapter } from '../../adapters/adaptersFactory'; -import { sleep } from '../../utils/utils'; -import { fillIncompletePowerSnapshots } from '../powerSnapshotServices'; - -const cronJobTime = - (config.get('UPDATE_POWER_ROUND_CRONJOB_EXPRESSION') as string) || - '0 0 * * *'; - -export const runUpdatePowerRoundCronJob = () => { - logger.debug( - 'runUpdatePowerRoundCronJob() has been called, cronJobTime', - cronJobTime, - ); - schedule(cronJobTime, async () => { - const fillSnapshotsRoundNumberPromise = fillIncompletePowerSnapshots(); - - const currentRound = await getPowerRound(); - const powerRound = getRoundNumberByDate(new Date()).round - 1; - logger.debug('runUpdatePowerRoundCronJob', { - powerRound, - currentRound, - 'powerRound !== currentRound?.round': powerRound !== currentRound?.round, - }); - let oldBottomRank; - if (powerRound !== currentRound?.round) { - logger.debug( - 'runUpdatePowerRoundCronJob copy rounds to previousRoundRank', - ); - await copyProjectRanksToPreviousRoundRankTable(); - await setPowerRound(powerRound); - oldBottomRank = await getBottomRank(); - } - - await fillSnapshotsRoundNumberPromise; - - await Promise.all([ - refreshProjectPowerView(), - refreshProjectFuturePowerView(), - refreshUserProjectPowerView(), - ]); - if (powerRound !== currentRound?.round) { - // Refreshing views need time to refresh tables, so I wait for 1 minute and after that check project rank changes - await sleep(120_000); - const projectThatTheirRankChanged = - await projectsThatTheirRanksHaveChanged(); - const newBottomRank = await getBottomRank(); - logger.debug('runUpdatePowerRoundCronJob projectThatTheirRankChanged', { - oldBottomRank, - newBottomRank, - projectThatTheirRankChanged: projectThatTheirRankChanged.filter( - item => - item.oldRank !== oldBottomRank || item.newRank !== newBottomRank, - ), - }); - await getNotificationAdapter().projectsHaveNewRank({ - oldBottomRank, - newBottomRank, - projectRanks: projectThatTheirRankChanged, - }); - } - }); -}; diff --git a/src/services/donationService.ts b/src/services/donationService.ts index 29c66c20b..e5bb1c1cf 100644 --- a/src/services/donationService.ts +++ b/src/services/donationService.ts @@ -23,7 +23,6 @@ import { getChainvineAdapter, getNotificationAdapter, } from '../adapters/adaptersFactory'; -import { calculateGivbackFactor } from './givbackService'; import SentryLogger from '../sentryLogger'; import { getUserDonationStats, @@ -33,7 +32,6 @@ import { import { refreshProjectEstimatedMatchingView } from './projectViewsService'; import { AppDataSource } from '../orm'; import { getQfRoundHistoriesThatDontHaveRelatedDonations } from '../repositories/qfRoundHistoryRepository'; -import { getPowerRound } from '../repositories/powerRoundRepository'; import { fetchSafeTransactionHash } from './safeServices'; import { NETWORKS_IDS_TO_NAME } from '../provider'; import { getTransactionInfoFromNetwork } from './chains'; @@ -94,12 +92,6 @@ export const updateDonationPricesAndValues = async ( token: token?.symbol, priceChainId, }); - const { givbackFactor, projectRank, bottomRankInRound, powerRound } = - await calculateGivbackFactor(project.id); - donation.givbackFactor = givbackFactor; - donation.projectRank = projectRank; - donation.bottomRankInRound = bottomRankInRound; - donation.powerRound = powerRound; return await donation.save(); }; @@ -294,7 +286,6 @@ export const syncDonationStatusWithBlockchainNetwork = async (params: { totalDonated: donationStats?.totalDonated, donationsCount: donationStats?.donationsCount, lastDonationDate: donationStats?.lastDonationDate, - GIVbacksRound: donation.powerRound + 1, // powerRound is 1 behind givbacks round QFDonor: donation.qfRound?.name, donationChain: NETWORKS_IDS_TO_NAME[donation.transactionNetworkId], }); @@ -404,7 +395,6 @@ export const insertDonationsFromQfRoundHistory = async (): Promise => { const qfRoundHistories = await getQfRoundHistoriesThatDontHaveRelatedDonations(); const donationDotEthAddress = '0x6e8873085530406995170Da467010565968C7C62'; // Address behind donation.eth ENS address; - const powerRound = (await getPowerRound())?.round || 1; if (qfRoundHistories.length === 0) { logger.debug( 'insertDonationsFromQfRoundHistory There is not any qfRoundHistories in DB that doesnt have related donation', @@ -459,7 +449,6 @@ export const insertDonationsFromQfRoundHistory = async (): Promise => { "amount", "valueUsd", "priceUsd", - "powerRound", "projectId", "distributedFundQfRoundId", "segmentNotified", @@ -476,7 +465,6 @@ export const insertDonationsFromQfRoundHistory = async (): Promise => { q."matchingFundAmount", q."matchingFund", q."matchingFundPriceUsd", - ${powerRound}, q."projectId", q."qfRoundId", true, diff --git a/src/services/givbackService.ts b/src/services/givbackService.ts deleted file mode 100644 index f94db6f1a..000000000 --- a/src/services/givbackService.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - findProjectPowerViewByProjectId, - getBottomRank, -} from '../repositories/projectPowerViewRepository'; -import { getPowerRound } from '../repositories/powerRoundRepository'; - -export const calculateGivbackFactor = async ( - projectId: number, -): Promise<{ - givbackFactor: number; - bottomRankInRound: number; - projectRank?: number; - powerRound: number; -}> => { - const minGivFactor = Number(process.env.GIVBACK_MIN_FACTOR); - const maxGivFactor = Number(process.env.GIVBACK_MAX_FACTOR); - const [projectPowerView, bottomRank, powerRound] = await Promise.all([ - findProjectPowerViewByProjectId(projectId), - getBottomRank(), - getPowerRound(), - ]); - - const eachRoundImpact = (maxGivFactor - minGivFactor) / (bottomRank - 1); - const givbackFactor = projectPowerView?.powerRank - ? minGivFactor + - eachRoundImpact * (bottomRank - projectPowerView?.powerRank) - : minGivFactor; - - return { - givbackFactor: givbackFactor || 0, - projectRank: projectPowerView?.powerRank, - bottomRankInRound: bottomRank, - powerRound: powerRound?.round as number, - }; -}; diff --git a/src/services/instantBoostingServices.test.ts b/src/services/instantBoostingServices.test.ts deleted file mode 100644 index fc2d093c3..000000000 --- a/src/services/instantBoostingServices.test.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { expect } from 'chai'; -import { PowerBoosting } from '../entities/powerBoosting'; -import { InstantPowerBalance } from '../entities/instantPowerBalance'; -import { updateInstantPowerBalances } from './instantBoostingServices'; -import { InstantPowerFetchState } from '../entities/instantPowerFetchState'; -import { getMaxFetchedUpdatedAtTimestamp } from '../repositories/instantBoostingRepository'; -import { insertSinglePowerBoosting } from '../repositories/powerBoostingRepository'; -import { - createProjectData, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, -} from '../../test/testUtils'; - -describe( - 'updateInstancePowerBalances test cases', - updateInstancePowerBalancesTestCase, -); - -const SampleStagingGivPowerUsers = [ - '0x00d18ca9782be1caef611017c2fbc1a39779a57c', - '0x05a1ff0a32bc24265bcb39499d0c5d9a6cb2011c', -]; - -// const getLastUpdatedUsers = async (): Promise => { -// return mockPowerBalanceAggregator.getBalancesUpdatedAfterDate({ -// date: 0, -// }); -// }; - -function updateInstancePowerBalancesTestCase() { - beforeEach(async () => { - await Promise.all([ - PowerBoosting.clear(), - InstantPowerBalance.clear(), - InstantPowerFetchState.clear(), - ]); - }); - - it('should not throw error on intact data', async () => { - await updateInstantPowerBalances(); - }); - - it('should update latest synced block', async () => { - await updateInstantPowerBalances(); - const maxTimestamp = await getMaxFetchedUpdatedAtTimestamp(); - expect(maxTimestamp).to.be.greaterThan(0); - }); - - it('should fill missing instant power balances of only boosters', async () => { - const firstUser = await saveUserDirectlyToDb(SampleStagingGivPowerUsers[0]); - // Second User - await saveUserDirectlyToDb(SampleStagingGivPowerUsers[1]); - - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - await insertSinglePowerBoosting({ - user: firstUser, - project: firstProject, - percentage: 2, - }); - - await updateInstantPowerBalances(); - - // Only the first user should have an instant power balance - const instantBalances = await InstantPowerBalance.find(); - expect(instantBalances.length).to.equal(1); - expect(instantBalances[0].userId).to.equal(firstUser.id); - expect(instantBalances[0].balanceAggregatorUpdatedAt).greaterThan( - new Date(0), - ); - }); - - /** - This test doesn't work because of the way we are mocking the adapter - */ - // it('should update instant power balances', async () => { - // const lastUpdatedUsers = await getLastUpdatedUsers(); - // /// Get last updated user - // const [firstUser, firstProject] = await Promise.all([ - // saveUserDirectlyToDb(lastUpdatedUsers[0].address), - // saveProjectDirectlyToDb(createProjectData()), - // ]); - // - // await insertSinglePowerBoosting({ - // user: firstUser, - // project: firstProject, - // percentage: 2, - // }); - // - // const beforeUpdateTimestamp = Number(lastUpdatedUsers[0].updatedAt) - 1; - // // Let's save last sync state some time behind user's last update time - // await setMaxFetchedUpdatedAtTimestamp(beforeUpdateTimestamp); - // - // // Set save firstUser instance power balance with incorrect value - // await saveOrUpdateInstantPowerBalances([ - // { - // userId: firstUser.id, - // balance: 0, - // balanceAggregatorUpdatedAt: new Date(beforeUpdateTimestamp), - // }, - // ]); - // - // await updateInstantPowerBalances(mockPowerBalanceAggregator); - // - // const instantBalances = await InstantPowerBalance.find(); - // expect(instantBalances.length).to.equal(1); - // const newBalance = instantBalances[0]; - // expect(newBalance.userId).to.equal(firstUser.id); - // expect(newBalance.balance).equal( - // formatGivPowerBalance(lastUpdatedUsers[0].balance), - // ); - // expect(newBalance.balanceAggregatorUpdatedAt).eq( - // lastUpdatedUsers[0].updatedAt, - // ); - // }); - // - // it('should update and create instant power balances', async () => { - // const lastUpdatedUsers = await getLastUpdatedUsers(); - // - // const userToUpdateUnipoolBalanceInfo = lastUpdatedUsers[0]; - // const userToCreateWalletAddress = - // SampleStagingGivPowerUsers[0] !== userToUpdateUnipoolBalanceInfo.address - // ? SampleStagingGivPowerUsers[0] - // : SampleStagingGivPowerUsers[1]; - // /// Get last updated user - // const [firstUser, secondUser, firstProject] = await Promise.all([ - // saveUserDirectlyToDb(userToUpdateUnipoolBalanceInfo.address), - // saveUserDirectlyToDb(userToCreateWalletAddress), - // saveProjectDirectlyToDb(createProjectData()), - // ]); - // - // const beforeUpdateTimestamp = - // dateToTimestampMs(userToUpdateUnipoolBalanceInfo.updatedAt) - 1; - // - // await Promise.all([ - // insertSinglePowerBoosting({ - // user: firstUser, - // project: firstProject, - // percentage: 2, - // }), - // insertSinglePowerBoosting({ - // user: secondUser, - // project: firstProject, - // percentage: 2, - // }), - // - // // Let's save last sync state some time behind user's last update time - // setMaxFetchedUpdatedAtTimestamp(beforeUpdateTimestamp), - // - // // Set save firstUser instance power balance with incorrect value - // saveOrUpdateInstantPowerBalances([ - // { - // userId: firstUser.id, - // balance: 0, - // balanceAggregatorUpdatedAt: new Date(beforeUpdateTimestamp), - // }, - // ]), - // ]); - // - // await updateInstantPowerBalances(mockPowerBalanceAggregator); - // - // const instantBalances = await InstantPowerBalance.find(); - // expect(instantBalances.length).to.equal(2); - // const firstUserUpdatedInstantPower = instantBalances.find( - // balance => balance.userId === firstUser.id, - // ); - // expect(firstUserUpdatedInstantPower?.userId).to.equal(firstUser.id); - // expect(firstUserUpdatedInstantPower?.balance).equal( - // formatGivPowerBalance(userToUpdateUnipoolBalanceInfo.balance), - // ); - // expect(firstUserUpdatedInstantPower?.balanceAggregatorUpdatedAt).eq( - // userToUpdateUnipoolBalanceInfo.updatedAt, - // ); - // - // const newBalance2 = instantBalances.find( - // balance => balance.userId === secondUser.id, - // ); - // expect(newBalance2?.userId).to.equal(secondUser.id); - // expect(newBalance2?.balanceAggregatorUpdatedAt).greaterThan(0); - // }); -} diff --git a/src/services/instantBoostingServices.ts b/src/services/instantBoostingServices.ts deleted file mode 100644 index 87463d001..000000000 --- a/src/services/instantBoostingServices.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { getPowerBalanceAggregatorAdapter } from '../adapters/adaptersFactory'; -import { - getMaxFetchedUpdatedAtTimestamp, - getUsersBoostedWithoutInstanceBalance, - refreshProjectInstantPowerView, - saveOrUpdateInstantPowerBalances, - setMaxFetchedUpdatedAtTimestamp, -} from '../repositories/instantBoostingRepository'; -import { logger } from '../utils/logger'; -import { getBoosterUsersByWalletAddresses } from '../repositories/powerBoostingRepository'; -import { dateToTimestampMs } from '../utils/utils'; -import { InstantPowerBalance } from '../entities/instantPowerBalance'; -import { - BalanceResponse, - IGivPowerBalanceAggregator, -} from '../types/GivPowerBalanceAggregator'; - -export const updateInstantBoosting = async (): Promise => { - logger.debug('updateInstantBoosting() has been called'); - try { - await updateInstantPowerBalances(); - } catch (e) { - logger.error( - 'updateInstantBoosting() calling updateInstantPowerBalances() error', - e, - ); - } - await refreshProjectInstantPowerView(); - // await refreshProjectUserInstantPowerView(); -}; - -// Allow passing a custom subgraph adapter for testing purposes -export const updateInstantPowerBalances = async ( - customGivPowerBalanceAggregator?: IGivPowerBalanceAggregator, -): Promise => { - logger.debug('Update instant power balances...'); - const givPowerSubgraphAdapter = - customGivPowerBalanceAggregator || getPowerBalanceAggregatorAdapter(); - await fetchUpdatedInstantPowerBalances(givPowerSubgraphAdapter); - await fillMissingInstantPowerBalances(givPowerSubgraphAdapter); -}; - -/** - * @param givPowerBalanceAggregator subgraph adapter - * Fetches power balances for users whose balance has been updated since last sync - */ -const fetchUpdatedInstantPowerBalances = async ( - givPowerBalanceAggregator: IGivPowerBalanceAggregator, -): Promise => { - logger.debug('1. Fetch updated instant powers'); - // Let's save it now to sync all balances till this point - // const [latestSubgraphIndexBlock, latestSyncedBlock] = await Promise.all([ - // givPowerBalanceAggregator.getLatestIndexedBlockInfo(), - // getLatestSyncedBlock(), - // ]); - let maxFetchedUpdatedAt = await getMaxFetchedUpdatedAtTimestamp(); - - // logger.debug(`Latest subgraph indexed block: ${latestSubgraphIndexBlock}`); - // logger.debug(`Latest synced block: ${latestSyncedBlock}`); - - let counter = 0; - // eslint-disable-next-line no-constant-condition - while (true) { - const balances = - await givPowerBalanceAggregator.getBalancesUpdatedAfterDate({ - date: maxFetchedUpdatedAt, - take: 1000, - skip: counter, - }); - - if (balances.length === 0) break; - - const addressBalanceMap: Record = {}; - balances.forEach(b => { - addressBalanceMap[b.address.toLowerCase()] = b; - }); - - const boosterUsers = await getBoosterUsersByWalletAddresses( - balances.map(b => b.address.toLowerCase()), - ); - const instances = boosterUsers.map(user => { - const walletAddress = user.walletAddress!.toLowerCase(); - const { balance, updatedAt } = addressBalanceMap[walletAddress]; - logger.debug( - `Update user ${user.id} - ${walletAddress} instant power balance to ${balance} - updateAt ${updatedAt}`, - ); - return { - balance, - balanceAggregatorUpdatedAt: updatedAt, - userId: user.id, - }; - }); - await saveOrUpdateInstantPowerBalances(instances); - const _maxFetchedUpdatedAt = dateToTimestampMs( - balances[balances.length - 1].updatedAt, - ); - - if (balances.length < 1000) { - maxFetchedUpdatedAt = _maxFetchedUpdatedAt; - break; - } else { - if (_maxFetchedUpdatedAt <= maxFetchedUpdatedAt) { - logger.error( - `maxFetchedUpdatedAt is not increasing maxFetchedUpdatedAt: ${maxFetchedUpdatedAt}, _maxFetchedUpdatedAt: ${_maxFetchedUpdatedAt}`, - ); - counter += balances.length; - } else { - // We will skip pagination and fetch 1000 again to avoid edge case issues - // https://github.com/Giveth/impact-graph/issues/1094#issue-1844862318 - maxFetchedUpdatedAt = _maxFetchedUpdatedAt - 1; - counter = 0; - } - } - } - - // Set synced block number to latest indexed block number - await setMaxFetchedUpdatedAtTimestamp(maxFetchedUpdatedAt); -}; - -/** - * @param givPowerSubgraphAdapter subgraph adapter - * Fetches power balances of users who has boosted but their balances are not in db - */ -const fillMissingInstantPowerBalances = async ( - givPowerSubgraphAdapter: IGivPowerBalanceAggregator, -): Promise => { - logger.debug('2. Fetch missing instant powers'); - // const latestSyncedBlock = await getMaxFetchedUpdatedAtTimestamp(); - // if (!latestSyncedBlock.timestamp) { - // logger.error( - // 'No latest block number found in db, cannot fetch missing balances\nAborting...', - // ); - // return; - // } - // - let allUsersWithoutBalance: { id: number; walletAddress: string }[] = []; - let counter = 0; - // eslint-disable-next-line no-constant-condition - while (true) { - const result = await getUsersBoostedWithoutInstanceBalance(100, counter); - if (result.length === 0) break; - allUsersWithoutBalance = [...allUsersWithoutBalance, ...result]; - counter += result.length; - } - - // iterate over allUsersWithoutBalance in chunks of 50 - // and get their balances from subgraph - // and save them in db - const chunkSize = 50; - - for (let i = 0; i < allUsersWithoutBalance.length; i += chunkSize) { - const chunk = allUsersWithoutBalance.slice(i, i + chunkSize); - const balances: BalanceResponse[] = - await givPowerSubgraphAdapter.getLatestBalances({ - addresses: chunk.map(user => user!.walletAddress.toLowerCase()), - }); - - const addressBalanceMap: Record = {}; - balances.forEach(b => { - addressBalanceMap[b.address.toLowerCase()] = b; - }); - - const instances: Partial[] = chunk.map< - Partial - >((item): Partial => { - const { balance, updatedAt } = addressBalanceMap[item.walletAddress]; - logger.debug( - `Update user ${item.id} - ${item.walletAddress} instant power balance to ${balance} - updateAt ${updatedAt}`, - ); - return { - balance, - balanceAggregatorUpdatedAt: updatedAt, - userId: item.id, - }; - }); - await saveOrUpdateInstantPowerBalances(instances); - } -}; diff --git a/src/services/powerBoostingService.test.ts b/src/services/powerBoostingService.test.ts deleted file mode 100644 index d0f3bac1b..000000000 --- a/src/services/powerBoostingService.test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { assert } from 'chai'; -import { - createProjectData, - generateRandomEtheriumAddress, - saveProjectDirectlyToDb, - saveUserDirectlyToDb, - sleep, -} from '../../test/testUtils'; -import { - findUserPowerBoosting, - setMultipleBoosting, -} from '../repositories/powerBoostingRepository'; -import { changeUserBoostingsAfterProjectCancelled } from './powerBoostingService'; - -describe( - 'changeUserBoostingsAfterProjectCancelled', - changeUserBoostingsAfterProjectCancelledTestCases, -); - -function changeUserBoostingsAfterProjectCancelledTestCases() { - it('should change user percentage to zero when project cancelled', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const user2 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const firstProject = await saveProjectDirectlyToDb(createProjectData()); - const projectThatWouldGetCancelled = - await saveProjectDirectlyToDb(createProjectData()); - await setMultipleBoosting({ - userId: user1.id, - projectIds: [firstProject.id, projectThatWouldGetCancelled.id], - percentages: [80, 20], - }); - await setMultipleBoosting({ - userId: user2.id, - projectIds: [firstProject.id, projectThatWouldGetCancelled.id], - percentages: [70, 30], - }); - await changeUserBoostingsAfterProjectCancelled({ - projectId: projectThatWouldGetCancelled.id, - }); - - // Changing percentages is async we sleep some milli seconds to make sure all updates has been done - await sleep(100); - const firstUserBoostings = await findUserPowerBoosting(user1.id); - const secondUserBoostings = await findUserPowerBoosting(user2.id); - - assert.equal(firstUserBoostings.length, 1); - assert.equal(secondUserBoostings.length, 1); - assert.equal(firstUserBoostings[0].percentage, 100); - assert.equal(firstUserBoostings[0].projectId, firstProject.id); - assert.equal(secondUserBoostings[0].percentage, 100); - assert.equal(secondUserBoostings[0].projectId, firstProject.id); - }); - it('should change user percentage to zero when project cancelled, even when just has 1 boositng', async () => { - const user1 = await saveUserDirectlyToDb(generateRandomEtheriumAddress()); - const projectThatWouldGetCancelled = - await saveProjectDirectlyToDb(createProjectData()); - await setMultipleBoosting({ - userId: user1.id, - projectIds: [projectThatWouldGetCancelled.id], - percentages: [100], - }); - - await changeUserBoostingsAfterProjectCancelled({ - projectId: projectThatWouldGetCancelled.id, - }); - - // Changing percentages is async we sleep some milli seconds to make sure all updates has been done - await sleep(100); - const firstUserBoostings = await findUserPowerBoosting(user1.id); - - assert.equal(firstUserBoostings.length, 0); - }); -} diff --git a/src/services/powerBoostingService.ts b/src/services/powerBoostingService.ts deleted file mode 100644 index 3b10a4145..000000000 --- a/src/services/powerBoostingService.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { - cancelProjectBoosting, - findPowerBoostings, -} from '../repositories/powerBoostingRepository'; - -export const changeUserBoostingsAfterProjectCancelled = async (params: { - projectId: number; -}) => { - const { projectId } = params; - const [powerBoostings] = await findPowerBoostings({ - orderBy: { direction: 'DESC', field: 'createdAt' }, - projectId, - }); - powerBoostings.forEach(powerBoosting => - cancelProjectBoosting({ - userId: powerBoosting.userId, - projectId, - }), - ); -}; diff --git a/src/services/powerSnapshotServices.ts b/src/services/powerSnapshotServices.ts deleted file mode 100644 index 800a9b0ff..000000000 --- a/src/services/powerSnapshotServices.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { - findInCompletePowerSnapShots, - updatePowerSnapShots, -} from '../repositories/powerSnapshotRepository'; -import { getRoundNumberByDate } from '../utils/powerBoostingUtils'; -import { logger } from '../utils/logger'; - -export const fillIncompletePowerSnapshots = async (): Promise => { - const incompletePowerSnapshots = await findInCompletePowerSnapShots(); - logger.debug( - 'fillIncompletePowerSnapshots incompletePowerSnapshots', - JSON.stringify(incompletePowerSnapshots, null, 2), - ); - for (const powerSnapshot of incompletePowerSnapshots) { - try { - const roundNumber = getRoundNumberByDate(powerSnapshot.time).round; - await updatePowerSnapShots({ - powerSnapshot, - roundNumber, - }); - } catch (e) { - logger.error('fillIncompletePowerSnapshots error', e); - } - } -}; diff --git a/src/services/recurringDonationService.ts b/src/services/recurringDonationService.ts index 3e2d52d41..4d3bba1b0 100644 --- a/src/services/recurringDonationService.ts +++ b/src/services/recurringDonationService.ts @@ -31,7 +31,6 @@ import { isTokenAcceptableForProject, updateDonationPricesAndValues, } from './donationService'; -import { calculateGivbackFactor } from './givbackService'; import { updateUserTotalDonated, updateUserTotalReceived } from './userService'; import config from '../config'; import { User } from '../entities/user'; @@ -228,13 +227,6 @@ export const createRelatedDonationsToStream = async ( donation.qfRoundUserScore = projectOwner?.passportScore; } - const { givbackFactor, projectRank, bottomRankInRound, powerRound } = - await calculateGivbackFactor(project.id); - donation.givbackFactor = givbackFactor; - donation.projectRank = projectRank; - donation.bottomRankInRound = bottomRankInRound; - donation.powerRound = powerRound; - await donation.save(); if (!donation.valueUsd || donation.valueUsd === 0) { diff --git a/src/types/GivPowerBalanceAggregator.ts b/src/types/GivPowerBalanceAggregator.ts deleted file mode 100644 index b70d55553..000000000 --- a/src/types/GivPowerBalanceAggregator.ts +++ /dev/null @@ -1,43 +0,0 @@ -export interface NetworksInputParams { - networks?: string; // comma separated sample: 100,11155420 - network?: string | number; // comma separated sample: 100 -} - -export interface LatestBalanceInputParams extends NetworksInputParams { - addresses: string[]; // sample: [walletAddress1,walletAddress2,..]} -} - -export interface BalancesAtTimestampInputParams - extends LatestBalanceInputParams { - timestamp: number; // sample: 1691739112 -} - -export interface BalanceResponse { - address: string; // Ethereum wallet address - balance: number; // sample: "121936497582050603356340" - updatedAt: Date; // sample: "2023-08-10T16:18:02.655Z" - networks: number[]; // sample: [100] -} - -export interface BalanceUpdatedAfterDateInputParams - extends NetworksInputParams { - date: Date | string | number; // sample: "2023-08-10T16:18:02.655Z" - take?: number; - skip?: number; -} - -export interface IGivPowerBalanceAggregator { - getAddressesBalance( - params: BalancesAtTimestampInputParams, - ): Promise; - - getLatestBalances( - params: LatestBalanceInputParams, - ): Promise; - - getBalancesUpdatedAfterDate( - params: BalanceUpdatedAfterDateInputParams, - ): Promise; - - getLeastIndexedBlockTimeStamp(params: NetworksInputParams): Promise; -} diff --git a/src/utils/errorMessages.ts b/src/utils/errorMessages.ts index 31e835498..253bcdac6 100644 --- a/src/utils/errorMessages.ts +++ b/src/utils/errorMessages.ts @@ -30,7 +30,6 @@ export const errorMessages = { ONRAMPER_SIGNATURE_INVALID: 'Onramper signature invalid', ONRAMPER_SIGNATURE_MISSING: 'Onramper signature missing', UPLOAD_FAILED: 'Upload file failed', - SPECIFY_GIV_POWER_ADAPTER: 'Specify givPower adapter', CHANGE_API_INVALID_TITLE_OR_EIN: 'ChangeAPI title or EIN not found or invalid', INVALID_SOCIAL_NETWORK: 'Invalid social network', @@ -173,18 +172,6 @@ export const errorMessages = { 'You are not the owner of social profile', ERROR_IN_GETTING_ACCESS_TOKEN_BY_AUTHORIZATION_CODE: 'Error in getting accessToken by authorization code', - ERROR_GIVPOWER_BOOSTING_FIRST_PROJECT_100_PERCENT: - 'First project boosting value must be 100%', - ERROR_GIVPOWER_BOOSTING_INVALID_DATA: 'Invalid data', - ERROR_GIV_POWER_BOOSTING_SUM_IS_GREATER_THAN_MAXIMUM: - 'Giv power boosting summation is greater than 100', - // ERROR_GIVPOWER_BOOSTING_PERCENTAGE_INVALID_RANGE: 'Invalid percentage value', - // ERROR_GIVPOWER_BOOSTING_MULTI_PERCENTAGE_INVALID_SUM: - // 'Sum of all boosting percentages must be between 99% to 100%', - // ERROR_GIVPOWER_BOOSTING_MULTISET_INVALID_DATA_LENGTH: - // 'Length of passed projects and percentages should be the same and more than zero', - ERROR_GIVPOWER_BOOSTING_MAX_PROJECT_LIMIT: - 'Number of boosted projects exceeds limit', REGISTERED_NON_PROFITS_CATEGORY_DOESNT_EXIST: 'There is not any category with name registered-non-profits, probably you forgot to run migrations', TEXT_IS_REQUIRED: '"text" is required', @@ -213,8 +200,7 @@ export const translationErrorMessagesKeys = { ONRAMPER_SIGNATURE_INVALID: 'ONRAMPER_SIGNATURE_INVALID', ONRAMPER_SIGNATURE_MISSING: 'ONRAMPER_SIGNATURE_MISSING', UPLOAD_FAILED: 'UPLOAD_FAILED', - SPECIFY_GIV_POWER_ADAPTER: 'SPECIFY_GIV_POWER_ADAPTER', - CHANGE_API_INVALID_TITLE_OR_EIN: 'SPECIFY_GIV_POWER_ADAPTER', + CHANGE_API_INVALID_TITLE_OR_EIN: 'CHANGE_API_INVALID_TITLE_OR_EIN', INVALID_SOCIAL_NETWORK: 'INVALID_SOCIAL_NETWORK', RECIPIENT_ADDRESSES_CANT_BE_EMPTY: 'RECIPIENT_ADDRESSES_CANT_BE_EMPTY', NOT_IMPLEMENTED: 'NOT_IMPLEMENTED', @@ -349,18 +335,6 @@ export const translationErrorMessagesKeys = { 'YOU_ARE_NOT_THE_OWNER_OF_THIS_SOCIAL_PROFILE', ERROR_IN_GETTING_ACCESS_TOKEN_BY_AUTHORIZATION_CODE: 'ERROR_IN_GETTING_ACCESS_TOKEN_BY_AUTHORIZATION_CODE', - ERROR_GIVPOWER_BOOSTING_FIRST_PROJECT_100_PERCENT: - 'ERROR_GIVPOWER_BOOSTING_FIRST_PROJECT_100_PERCENT', - ERROR_GIVPOWER_BOOSTING_INVALID_DATA: 'ERROR_GIVPOWER_BOOSTING_INVALID_DATA', - ERROR_GIV_POWER_BOOSTING_SUM_IS_GREATER_THAN_MAXIMUM: - 'ERROR_GIV_POWER_BOOSTING_SUM_IS_GREATER_THAN_MAXIMUM', - // ERROR_GIVPOWER_BOOSTING_PERCENTAGE_INVALID_RANGE: 'Invalid percentage value', - // ERROR_GIVPOWER_BOOSTING_MULTI_PERCENTAGE_INVALID_SUM: - // 'Sum of all boosting percentages must be between 99% to 100%', - // ERROR_GIVPOWER_BOOSTING_MULTISET_INVALID_DATA_LENGTH: - // 'Length of passed projects and percentages should be the same and more than zero', - ERROR_GIVPOWER_BOOSTING_MAX_PROJECT_LIMIT: - 'ERROR_GIVPOWER_BOOSTING_MAX_PROJECT_LIMIT', REGISTERED_NON_PROFITS_CATEGORY_DOESNT_EXIST: 'REGISTERED_NON_PROFITS_CATEGORY_DOESNT_EXIST', PROJECT_UPDATE_CONTENT_LENGTH_SIZE_EXCEEDED: diff --git a/src/utils/locales/en.json b/src/utils/locales/en.json index 743b7ecbc..eaa654299 100644 --- a/src/utils/locales/en.json +++ b/src/utils/locales/en.json @@ -6,7 +6,6 @@ "ONRAMPER_SIGNATURE_INVALID": "Request payload or signature is invalid", "ONRAMPER_SIGNATURE_MISSING": "Request headers does not contain signature", "UPLOAD_FAILED": "Upload file failed", - "SPECIFY_GIV_POWER_ADAPTER": "Specify givPower adapter", "CHANGE_API_INVALID_TITLE_OR_EIN": "ChangeAPI title or EIN not found or invalid", "INVALID_SOCIAL_NETWORK": "Invalid social network", "RECIPIENT_ADDRESSES_CANT_BE_EMPTY": "Recipient addresses can't be empty", @@ -96,10 +95,6 @@ "SOCIAL_PROFILE_IS_ALREADY_VERIFIED": "Social profile is already verified", "YOU_ARE_NOT_THE_OWNER_OF_THIS_SOCIAL_PROFILE": "You are not the owner of social profile", "ERROR_IN_GETTING_ACCESS_TOKEN_BY_AUTHORIZATION_CODE": "Error in getting accessToken by authorization code", - "ERROR_GIVPOWER_BOOSTING_FIRST_PROJECT_100_PERCENT": "First project boosting value must be 100%", - "ERROR_GIVPOWER_BOOSTING_INVALID_DATA": "Invalid data", - "ERROR_GIV_POWER_BOOSTING_SUM_IS_GREATER_THAN_MAXIMUM": "Giv power boosting summation is greater than 100", - "ERROR_GIVPOWER_BOOSTING_MAX_PROJECT_LIMIT": "Number of boosted projects exceeds limit", "REGISTERED_NON_PROFITS_CATEGORY_DOESNT_EXIST": "There is not any category with name registered-non-profits, probably you forgot to run migrations", "PROJECT_UPDATE_CONTENT_LENGTH_SIZE_EXCEEDED": "Content length exceeded", "INVALID_TOKEN_ADDRESS": "Invalid tokenAddress", diff --git a/src/utils/locales/es.json b/src/utils/locales/es.json index 1292916d6..cda161a0f 100644 --- a/src/utils/locales/es.json +++ b/src/utils/locales/es.json @@ -6,7 +6,6 @@ "ONRAMPER_SIGNATURE_INVALID": "El cuerpo o firma son invalidos", "ONRAMPER_SIGNATURE_MISSING": "El encabezado no continue la firma", "UPLOAD_FAILED": "No fue posible subir el archivo", - "SPECIFY_GIV_POWER_ADAPTER": "Especificar adaptador givPower", "CHANGE_API_INVALID_TITLE_OR_EIN": "ChangeAPI título de API o EIN no encontrado o no válido", "INVALID_SOCIAL_NETWORK": "Red social inválida", "IT_SHOULD_HAVE_ONE_OR_TWO_ADDRESSES_FOR_RECIPIENT": "Las direcciones de los destinatarios no pueden estar vacías", @@ -97,10 +96,6 @@ "SOCIAL_PROFILE_IS_ALREADY_VERIFIED": "El perfil social ya está verificado", "YOU_ARE_NOT_THE_OWNER_OF_THIS_SOCIAL_PROFILE": "No eres el dueño de esta red social", "ERROR_IN_GETTING_ACCESS_TOKEN_BY_AUTHORIZATION_CODE": "Error al obtener token de acceso por código de autorización", - "ERROR_GIVPOWER_BOOSTING_FIRST_PROJECT_100_PERCENT": "El valor del Boost del primer proyecto debe ser del 100 %", - "ERROR_GIVPOWER_BOOSTING_INVALID_DATA": "Datos inválidos", - "ERROR_GIV_POWER_BOOSTING_SUM_IS_GREATER_THAN_MAXIMUM": "La suma de aumento de potencia de Giv es mayor que 100", - "ERROR_GIVPOWER_BOOSTING_MAX_PROJECT_LIMIT": "Número de proyectos impulsados excede el límite", "REGISTERED_NON_PROFITS_CATEGORY_DOESNT_EXIST": "No hay ninguna categoría con nombre registrado-sin fines de lucro, probablemente se olvidó de ejecutar las migraciones", "PROJECT_UPDATE_CONTENT_LENGTH_SIZE_EXCEEDED": "El contenido es demasiado largo", "DRAFT_DONATION_DISABLED": "El borrador de donación está deshabilitado", diff --git a/src/views/lastSnapshotProjectPowerView.ts b/src/views/lastSnapshotProjectPowerView.ts deleted file mode 100644 index 426bf2797..000000000 --- a/src/views/lastSnapshotProjectPowerView.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - BaseEntity, - Column, - Index, - PrimaryColumn, - ViewColumn, - ViewEntity, -} from 'typeorm'; -import { Field, Float, Int, ObjectType } from 'type-graphql'; -import { ColumnNumericTransformer } from '../utils/entities'; - -@ViewEntity('last_snapshot_project_power_view', { synchronize: false }) -@ObjectType() -export class LastSnapshotProjectPowerView extends BaseEntity { - @Field() - @ViewColumn() - @PrimaryColumn() - projectId: number; - - @ViewColumn() - @Field(_type => Float) - @Column('numeric', { - scale: 2, - transformer: new ColumnNumericTransformer(), - }) - totalPower: number; - - @ViewColumn() - // Not sure why queries return type was string! - @Field(_type => String) - @Index() - powerRank: string; - - @ViewColumn() - @Field(_type => Int) - round: number; -} diff --git a/src/views/projectFuturePowerView.ts b/src/views/projectFuturePowerView.ts deleted file mode 100644 index ca613506e..000000000 --- a/src/views/projectFuturePowerView.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - OneToOne, - ViewColumn, - ViewEntity, - JoinColumn, - RelationId, - BaseEntity, - PrimaryColumn, -} from 'typeorm'; -import { Field, Int, ObjectType } from 'type-graphql'; -import { Project } from '../entities/project'; - -@ViewEntity('project_future_power_view', { synchronize: false }) -@ObjectType() -export class ProjectFuturePowerView extends BaseEntity { - @Field() - @ViewColumn() - @PrimaryColumn() - @RelationId( - (projectFuturePowerView: ProjectFuturePowerView) => - projectFuturePowerView.project, - ) - projectId: number; - - @ViewColumn() - @Field() - totalPower: number; - - @Field(_type => Project) - @OneToOne(_type => Project, project => project.projectFuturePower) - @JoinColumn({ referencedColumnName: 'id' }) - project: Project; - - @ViewColumn() - @Field(_type => Int) - powerRank: number; - - @ViewColumn() - @Field(_type => Int) - round: number; -} diff --git a/src/views/projectInstantPowerView.ts b/src/views/projectInstantPowerView.ts deleted file mode 100644 index 7787ceff0..000000000 --- a/src/views/projectInstantPowerView.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - OneToOne, - ViewColumn, - ViewEntity, - JoinColumn, - RelationId, - BaseEntity, - PrimaryColumn, - Column, -} from 'typeorm'; -import { Field, Float, ObjectType } from 'type-graphql'; -import { Project } from '../entities/project'; -import { ColumnNumericTransformer } from '../utils/entities'; - -@ViewEntity('project_instant_power_view', { synchronize: false }) -@ObjectType() -export class ProjectInstantPowerView extends BaseEntity { - @Field() - @ViewColumn() - @PrimaryColumn() - @RelationId( - (projectPowerView: ProjectInstantPowerView) => projectPowerView.project, - ) - projectId: number; - - @ViewColumn() - @Field(_type => Float) - @Column('numeric', { - scale: 2, - transformer: new ColumnNumericTransformer(), - }) - totalPower: number; - - @Field(_type => Project) - @OneToOne(_type => Project, project => project.projectPower) - @JoinColumn({ referencedColumnName: 'id' }) - project: Project; - - @ViewColumn() - @Field() - powerRank: string; -} diff --git a/src/views/projectPowerView.ts b/src/views/projectPowerView.ts deleted file mode 100644 index b711cbdcf..000000000 --- a/src/views/projectPowerView.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - OneToOne, - ViewColumn, - ViewEntity, - JoinColumn, - RelationId, - BaseEntity, - PrimaryColumn, - Column, - Index, -} from 'typeorm'; -import { Field, Float, Int, ObjectType } from 'type-graphql'; -import { Project } from '../entities/project'; -import { ColumnNumericTransformer } from '../utils/entities'; - -@ViewEntity('project_power_view', { synchronize: false }) -@Index('project_power_view_project_id_unique', ['projectId', 'round'], { - unique: true, -}) -@ObjectType() -export class ProjectPowerView extends BaseEntity { - @Field() - @ViewColumn() - @PrimaryColumn() - @RelationId((projectPowerView: ProjectPowerView) => projectPowerView.project) - projectId: number; - - @ViewColumn() - @Field(_type => Float) - @Column('numeric', { - scale: 2, - transformer: new ColumnNumericTransformer(), - }) - totalPower: number; - - @Field(_type => Project) - @OneToOne(_type => Project, project => project.projectPower) - @JoinColumn({ referencedColumnName: 'id' }) - project: Project; - - @ViewColumn() - @Field(_type => Int) - powerRank: number; - - @ViewColumn() - @Field(_type => Int) - round: number; -} diff --git a/src/views/projectUserInstantPowerView.ts b/src/views/projectUserInstantPowerView.ts deleted file mode 100644 index 0e1150fc8..000000000 --- a/src/views/projectUserInstantPowerView.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - ViewColumn, - ViewEntity, - BaseEntity, - PrimaryColumn, - Column, - ManyToOne, -} from 'typeorm'; -import { Field, Float, ObjectType } from 'type-graphql'; -import { ColumnNumericTransformer } from '../utils/entities'; -import { User } from '../entities/user'; - -@ViewEntity('project_user_instant_power_view', { synchronize: false }) -@ObjectType() -export class ProjectUserInstantPowerView extends BaseEntity { - @Field() - @ViewColumn() - @PrimaryColumn() - id: number; - - @ViewColumn() - @Field() - projectId: number; - - @Field(_type => User) - @ManyToOne(_type => User, { eager: true }) - user?: User; - - @ViewColumn() - @Field() - userId: number; - - @ViewColumn() - @Field(_type => Float) - @Column('numeric', { - scale: 2, - transformer: new ColumnNumericTransformer(), - }) - boostedPower: number; -} diff --git a/src/views/userProjectPowerView.ts b/src/views/userProjectPowerView.ts deleted file mode 100644 index c74348c30..000000000 --- a/src/views/userProjectPowerView.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - BaseEntity, - Column, - JoinColumn, - ManyToOne, - PrimaryColumn, - RelationId, - ViewColumn, - ViewEntity, -} from 'typeorm'; -import { Field, ObjectType } from 'type-graphql'; -import { User } from '../entities/user'; - -@ViewEntity('user_project_power_view', { - synchronize: false, -}) -@ObjectType() -export class UserProjectPowerView extends BaseEntity { - @PrimaryColumn() - @Field() - // it's the powerBoostingId see the migration creation file to understand better - id: number; - - @Field(_type => User, { nullable: true }) - @JoinColumn({ referencedColumnName: 'id' }) - @ManyToOne(() => User, { eager: true }) - user?: User; - - @ViewColumn() - @Field() - @RelationId((userProjectView: UserProjectPowerView) => userProjectView.user) - userId: number; - - @ViewColumn() - @Field() - projectId: number; - - @ViewColumn() - @Field() - round: number; - - @ViewColumn() - @Field() - boostedPower: number; - - @Field() - @Column({ - select: false, - nullable: true, - insert: false, - update: false, - }) - rank?: number; -} diff --git a/test/graphqlQueries.ts b/test/graphqlQueries.ts index e46d0dcfb..f2894c2e3 100644 --- a/test/graphqlQueries.ts +++ b/test/graphqlQueries.ts @@ -829,11 +829,6 @@ export const fetchFeaturedProjects = ` networkId chainType } - projectPower { - totalPower - powerRank - round - } totalReactions totalDonations totalTraceDonations @@ -935,15 +930,6 @@ export const fetchMultiFilterAllProjectsQuery = ` networkId chainType } - projectPower { - totalPower - powerRank - round - } - projectInstantPower { - totalPower - powerRank - } qfRounds { name isActive @@ -1070,19 +1056,6 @@ export const fetchProjectBySlugQuery = ` updatedAt } givbackFactor - projectPower { - totalPower - powerRank - round - } - projectInstantPower { - totalPower - } - projectFuturePower { - totalPower - powerRank - round - } categories { name mainCategory { @@ -1320,7 +1293,6 @@ export const userByAddress = ` url location isSignedIn - boostedProjectsCount likedProjectsCount donationsCount totalDonated @@ -1343,7 +1315,6 @@ export const refreshUserScores = ` walletAddress url location - boostedProjectsCount likedProjectsCount donationsCount projectsCount @@ -2098,138 +2069,6 @@ export const getCategoryData = `query { } }`; -export const setSinglePowerBoostingMutation = ` - mutation ($projectId: Int!, $percentage: Float!) { - setSinglePowerBoosting(projectId: $projectId, percentage: $percentage) { - id - user { - id - } - project { - id - } - percentage - } - } - `; - -export const setMultiplePowerBoostingMutation = ` - mutation ($projectIds: [Int!]!, $percentages: [Float!]!) { - setMultiplePowerBoosting(projectIds: $projectIds, percentages: $percentages) { - id - user { - id - } - project { - id - } - percentage - } - } - `; - -export const getPowerBoostingsQuery = ` - query ( - $take: Int - $skip: Int - $orderBy: PowerBoostingOrderBy - $projectId: Int - $userId: Int - ) { - getPowerBoosting( - take: $take - skip: $skip - orderBy: $orderBy - projectId: $projectId - userId: $userId - ) { - powerBoostings { - id - updatedAt - createdAt - user { - id - email - } - project { - id - } - percentage - } - } - } -`; - -export const getUserProjectPowerQuery = ` - query ( - $take: Int - $skip: Int - $orderBy: UserPowerOrderBy - $projectId: Int - $userId: Int - ) { - userProjectPowers ( - take: $take - skip: $skip - orderBy: $orderBy - projectId: $projectId - userId: $userId - ) { - totalCount - userProjectPowers { - id - userId - projectId - round - boostedPower - rank - user { - id - firstName - lastName - name - } - - } - } - } -`; - -export const getBottomPowerRankQuery = ` - query { - getTopPowerRank - } -`; - -export const getPowerAmountRankQuery = ` - query ( - $powerAmount: Float! - $projectId: Int - ) { - powerAmountRank(powerAmount: $powerAmount, projectId: $projectId) - } -`; - -export const getProjectUserInstantPowerQuery = ` - query ($projectId: Int!, $take: Int, $skip: Int) - { - getProjectUserInstantPower (projectId: $projectId, take: $take, skip: $skip) { - projectUserInstantPowers { - id - userId - projectId - boostedPower - user { - name - walletAddress - avatar - } - } - total - } - } - `; - export const doesDonatedToProjectInQfRoundQuery = ` query ( $projectId: Int!, diff --git a/test/pre-test-scripts.ts b/test/pre-test-scripts.ts index e5316236c..e91023941 100644 --- a/test/pre-test-scripts.ts +++ b/test/pre-test-scripts.ts @@ -20,28 +20,18 @@ import { } from '../src/entities/organization'; import { NETWORK_IDS } from '../src/provider'; import { MainCategory } from '../src/entities/mainCategory'; -import { TakePowerBoostingSnapshotProcedure1663594895751 } from '../migration/1663594895751-takePowerSnapshotProcedure'; -import { createGivPowerHistoricTablesProcedure1670429143091 } from '../migration/1670429143091-createGivPowerHistoricTablesProcedure'; import { AppDataSource } from '../src/orm'; import { createOrganisatioTokenTable1646302349926 } from '../migration/1646302349926-createOrganisatioTokenTable'; -import { TakePowerBoostingSnapshotProcedureSecondVersion1690723242749 } from '../migration/1690723242749-TakePowerBoostingSnapshotProcedureSecondVersion'; import { redis } from '../src/redis'; import { logger } from '../src/utils/logger'; -import { addCoingeckoIdAndCryptoCompareIdToEtcTokens1697959345387 } from '../migration/1697959345387-addCoingeckoIdAndCryptoCompareIdToEtcTokens'; -import { addIsStableCoinFieldToTokenTable1696421249293 } from '../migration/1696421249293-add_isStableCoin_field_to_token_table'; -import { createDonationethUser1701756190381 } from '../migration/1701756190381-create_donationeth_user'; import { ChainType } from '../src/types/network'; import { COINGECKO_TOKEN_IDS } from '../src/adapters/price/CoingeckoPriceAdapter'; import { EnablePgTrgmExtension1713859866338 } from '../migration/1713859866338-enable_pg_trgm_extension'; import { AddPgTrgmIndexes1715086559930 } from '../migration/1715086559930-add_pg_trgm_indexes'; -import { ProjectPowerViewV21717643739652 } from '../migration/1717643739652-ProjectPowerView_V2'; +import { addIsStableCoinFieldToTokenTable1696421249293 } from '../migration/1696421249293-add_isStableCoin_field_to_token_table'; import { ProjectEstimatedMatchingViewV21717646357435 } from '../migration/1717646357435-ProjectEstimatedMatchingView_V2'; import { ProjectActualMatchingViewV161717646612482 } from '../migration/1717646612482-ProjectActualMatchingView_V16'; -import { LastSnapshotProjectPowerViewV21717648491606 } from '../migration/1717648491606-LastSnapshotProjectPowerView_V2'; -import { ProjectFuturePowerViewV21717643016553 } from '../migration/1717643016553-ProjectFuturePowerView_V2'; -import { ProjectUserInstantPowerViewV21717644442966 } from '../migration/1717644442966-ProjectUserInstantPowerView_V2'; -import { ProjectInstantPowerViewV21717648653115 } from '../migration/1717648653115-ProjectInstantPowerView_V2'; -import { UserProjectPowerViewV21717645768886 } from '../migration/1717645768886-UserProjectPowerView_V2'; +import { createDonationethUser1701756190381 } from '../migration/1701756190381-create_donationeth_user'; async function seedDb() { await seedUsers(); @@ -528,26 +518,10 @@ async function runMigrations() { await queryRunner.connect(); try { - await new UserProjectPowerViewV21717645768886().up(queryRunner); - await new ProjectPowerViewV21717643739652().up(queryRunner); - await new LastSnapshotProjectPowerViewV21717648491606().up(queryRunner); - await new ProjectFuturePowerViewV21717643016553().up(queryRunner); - await new TakePowerBoostingSnapshotProcedure1663594895751().up(queryRunner); - await new createGivPowerHistoricTablesProcedure1670429143091().up( - queryRunner, - ); await new createOrganisatioTokenTable1646302349926().up(queryRunner); - await new ProjectInstantPowerViewV21717648653115().up(queryRunner); - await new ProjectEstimatedMatchingViewV21717646357435().up(queryRunner); - await new ProjectUserInstantPowerViewV21717644442966().up(queryRunner); - await new TakePowerBoostingSnapshotProcedureSecondVersion1690723242749().up( - queryRunner, - ); await new addIsStableCoinFieldToTokenTable1696421249293().up(queryRunner); - await new addCoingeckoIdAndCryptoCompareIdToEtcTokens1697959345387().up( - queryRunner, - ); await new createDonationethUser1701756190381().up(queryRunner); + await new ProjectEstimatedMatchingViewV21717646357435().up(queryRunner); await new ProjectActualMatchingViewV161717646612482().up(queryRunner); await new EnablePgTrgmExtension1713859866338().up(queryRunner); await new AddPgTrgmIndexes1715086559930().up(queryRunner);