Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore!: refactoring quiz for it to be time based #3732

Merged
merged 9 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions bats/core/api/earn.bats → bats/core/api/quiz.bats
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ setup_file() {
"alice.btc_wallet_id" \
"funder.btc_wallet_id" \
"10000"

}

@test "earn: completes a quiz question and gets paid once" {
@test "quiz: completes a quiz question and gets paid once" {
token_name="alice"
question_id="sat"

Expand All @@ -29,7 +28,11 @@ setup_file() {
.balance
')

# Do earn
exec_graphql $token_name 'quiz'
completed=$(graphql_output '.data.me.defaultAccount.quiz' | jq '.[] | select(.id == "sat") | .completed')
[[ "${completed}" == "false" ]] || exit 1

# Do quiz
variables=$(
jq -n \
--arg question_id "$question_id" \
Expand All @@ -41,21 +44,25 @@ setup_file() {
quiz_completed=$(graphql_output '.data.quizCompleted.quiz.completed')
[[ "${quiz_completed}" == "true" ]] || exit 1

exec_graphql $token_name 'quiz'
completed=$(graphql_output '.data.me.defaultAccount.quiz' | jq '.[] | select(.id == "sat") | .completed')
[[ "${completed}" == "true" ]] || exit 1

# Check balance after complete
exec_graphql $token_name 'wallets-for-account'
btc_balance_after_earn=$(graphql_output '
btc_balance_after_quiz=$(graphql_output '
.data.me.defaultAccount.wallets[]
| select(.walletCurrency == "BTC")
.balance
')
[[ "$btc_balance_after_earn" -gt "$btc_initial_balance" ]] || exit 1
[[ "$btc_balance_after_quiz" -gt "$btc_initial_balance" ]] || exit 1

# Check memo
exec_graphql "$token_name" 'transactions' '{"first": 1}'
txn_memo=$(graphql_output '.data.me.defaultAccount.transactions.edges[0].node.memo')
[[ "${txn_memo}" == "${question_id}" ]] || exit 1

# Retry earn
# Retry quiz
exec_graphql "$token_name" 'quiz-question' "$variables"
errors=$(graphql_output '.data.quizCompleted.errors')
[[ "${errors}" != "null" ]] || exit 1
Expand All @@ -69,5 +76,5 @@ setup_file() {
| select(.walletCurrency == "BTC")
.balance
')
[[ "$btc_balance_after_retry" == "$btc_balance_after_earn" ]] || exit 1
[[ "$btc_balance_after_retry" == "$btc_balance_after_quiz" ]] || exit 1
}
15 changes: 15 additions & 0 deletions bats/gql/quiz.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
query myQuizQuestions {
me {
id
defaultAccount {
id
... on ConsumerAccount {
quiz {
id
amount
completed
}
}
}
}
}
3 changes: 3 additions & 0 deletions core/api/src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as AuthenticationMod from "./authentication"
import * as AdminMod from "./admin"
import * as CallbackMod from "./callback"
import * as CommMod from "./comm"
import * as QuizMod from "./quiz"
import * as LightningMod from "./lightning"
import * as OnChainMod from "./on-chain"
import * as PricesMod from "./prices"
Expand All @@ -20,6 +21,7 @@ const allFunctions = {
Admin: { ...AdminMod },
Callback: { ...CallbackMod },
Comm: { ...CommMod },
Quiz: { ...QuizMod },
Lightning: { ...LightningMod },
OnChain: { ...OnChainMod },
Prices: { ...PricesMod },
Expand Down Expand Up @@ -49,6 +51,7 @@ export const {
Admin,
Callback,
Comm,
Quiz,
Lightning,
OnChain,
Prices,
Expand Down
1 change: 0 additions & 1 deletion core/api/src/app/payments/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from "./add-earn"
export * from "./get-protocol-fee"
export * from "./reimburse-fee"
export * from "./send-intraledger"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { intraledgerPaymentSendWalletIdForBtcWallet } from "./send-intraledger"
import { intraledgerPaymentSendWalletIdForBtcWallet } from "../payments/send-intraledger"

import { getRewardsConfig, OnboardingEarn } from "@/config"
import { QuizzesValue } from "@/domain/earn"

import { getQuizzesConfig } from "@/config"

import { getBalanceForWallet } from "@/app/wallets"

Expand All @@ -9,29 +11,29 @@ import {
InvalidQuizQuestionIdError,
MissingIPMetadataError,
NoBtcWalletExistsForAccountError,
NotEnoughBalanceForRewardError,
NotEnoughBalanceForQuizError,
UnauthorizedIPError,
UnknownRepositoryError,
} from "@/domain/errors"
import { WalletCurrency } from "@/domain/shared"
import { RateLimitConfig } from "@/domain/rate-limit"
import { checkedToAccountId } from "@/domain/accounts"
import { PhoneMetadataAuthorizer } from "@/domain/users"
import { InvalidPhoneForRewardError } from "@/domain/users/errors"
import { InvalidPhoneForQuizError } from "@/domain/users/errors"
import { RateLimiterExceededError } from "@/domain/rate-limit/errors"
import { IPMetadataAuthorizer } from "@/domain/accounts-ips/ip-metadata-authorizer"

import {
AccountsRepository,
RewardsRepository,
QuizRepository,
WalletsRepository,
UsersRepository,
} from "@/services/mongoose"
import { consumeLimiter } from "@/services/rate-limit"
import { getFunderWalletId } from "@/services/ledger/caching"
import { AccountsIpsRepository } from "@/services/mongoose/accounts-ips"

export const addEarn = async ({
export const completeQuiz = async ({
quizQuestionId: quizQuestionIdString,
accountId: accountIdRaw,
ip,
Expand All @@ -40,18 +42,18 @@ export const addEarn = async ({
accountId: string
ip: IpAddress | undefined
}): Promise<QuizQuestion | ApplicationError> => {
const check = await checkAddEarnAttemptPerIpLimits(ip)
const check = await checkAddQuizAttemptPerIpLimits(ip)
if (check instanceof Error) return check

const accountId = checkedToAccountId(accountIdRaw)
if (accountId instanceof Error) return accountId

const rewardsConfig = getRewardsConfig()
const quizzesConfig = getQuizzesConfig()

// TODO: quizQuestionId checkedFor
const quizQuestionId = quizQuestionIdString as QuizQuestionId
const quizId = quizQuestionIdString as QuizQuestionId

const amount = OnboardingEarn[quizQuestionId]
const amount = QuizzesValue[quizId]
if (!amount) return new InvalidQuizQuestionIdError()

const funderWalletId = await getFunderWalletId()
Expand All @@ -67,26 +69,26 @@ export const addEarn = async ({
if (user instanceof Error) return user

const validatedPhoneMetadata = PhoneMetadataAuthorizer(
rewardsConfig.phoneMetadataValidationSettings,
quizzesConfig.phoneMetadataValidationSettings,
).authorize(user.phoneMetadata)

if (validatedPhoneMetadata instanceof Error) {
return new InvalidPhoneForRewardError(validatedPhoneMetadata.name)
return new InvalidPhoneForQuizError(validatedPhoneMetadata.name)
}

const accountIP = await AccountsIpsRepository().findLastByAccountId(recipientAccount.id)
if (accountIP instanceof Error) return accountIP

const validatedIPMetadata = IPMetadataAuthorizer(
rewardsConfig.ipMetadataValidationSettings,
quizzesConfig.ipMetadataValidationSettings,
).authorize(accountIP.metadata)
if (validatedIPMetadata instanceof Error) {
if (validatedIPMetadata instanceof MissingIPMetadataError)
return new InvalidIpMetadataError(validatedIPMetadata)

if (validatedIPMetadata instanceof UnauthorizedIPError) return validatedIPMetadata

return new UnknownRepositoryError("add earn error")
return new UnknownRepositoryError("add quiz error")
}

const recipientWallets = await WalletsRepository().listByAccountId(accountId)
Expand All @@ -98,8 +100,8 @@ export const addEarn = async ({
if (recipientBtcWallet === undefined) return new NoBtcWalletExistsForAccountError()
const recipientWalletId = recipientBtcWallet.id

const shouldGiveReward = await RewardsRepository(accountId).add(quizQuestionId)
if (shouldGiveReward instanceof Error) return shouldGiveReward
const shouldGiveSats = await QuizRepository().add({ quizId, accountId })
if (shouldGiveSats instanceof Error) return shouldGiveSats

const funderBalance = await getBalanceForWallet({ walletId: funderWalletId })
if (funderBalance instanceof Error) return funderBalance
Expand All @@ -114,21 +116,21 @@ export const addEarn = async ({
senderWalletId: funderWalletId,
recipientWalletId,
amount,
memo: quizQuestionId,
memo: quizId,
senderAccount: funderAccount,
})
if (payment instanceof Error) return payment

return { id: quizQuestionId, earnAmount: amount }
return { id: quizId, earnAmount: amount }
}

const checkAddEarnAttemptPerIpLimits = async (
const checkAddQuizAttemptPerIpLimits = async (
ip: IpAddress | undefined,
): Promise<true | RateLimiterExceededError> => {
if (!ip) return new InvalidIpMetadataError()

return consumeLimiter({
rateLimitConfig: RateLimitConfig.addEarnAttemptPerIp,
rateLimitConfig: RateLimitConfig.addQuizAttemptPerIp,
keyToConsume: ip,
})
}
Expand All @@ -142,7 +144,7 @@ const FunderBalanceChecker = () => {
amountToSend: Satoshis
}): ValidationError | true => {
if (balance < amountToSend) {
return new NotEnoughBalanceForRewardError(JSON.stringify({ balance, amountToSend }))
return new NotEnoughBalanceForQuizError(JSON.stringify({ balance, amountToSend }))
}

return true
Expand Down
17 changes: 17 additions & 0 deletions core/api/src/app/quiz/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { QuizzesValue } from "@/domain/earn/config"
import { QuizRepository } from "@/services/mongoose"

export const getQuizzesByAccountId = async (accountId: AccountId) => {
const quizzes = await QuizRepository().fetchAll(accountId)
if (quizzes instanceof Error) return quizzes

const solvedQuizId = quizzes.map((quiz) => quiz.quizId)

const result = Object.entries(QuizzesValue).map(([id, amount]) => ({
id: id as QuizQuestionId,
amount,
completed: solvedQuizId.indexOf(id) > -1,
}))

return result
}
2 changes: 2 additions & 0 deletions core/api/src/app/quiz/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./add"
export * from "./get"
Loading
Loading