Skip to content

Commit

Permalink
feat: enable quiz completion without reward
Browse files Browse the repository at this point in the history
  • Loading branch information
UncleSamtoshi committed Oct 25, 2023
1 parent a80691b commit 2da4245
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 24 deletions.
1 change: 1 addition & 0 deletions core/api/dev/apollo-federation/supergraph.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ type ConsumerAccount implements Account

"""List the quiz questions of the consumer account"""
quiz: [Quiz!]!
quizRewardsEnabled: Boolean!
realtimePrice: RealtimePrice!

"""
Expand Down
99 changes: 85 additions & 14 deletions core/api/src/app/payments/add-earn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ export const addEarn = async ({
}: {
quizQuestionId: string
accountId: string
}): Promise<QuizQuestion | ApplicationError> => {
}): Promise<
| {
quizQuestion: Quiz
rewardPaymentErrror?: ApplicationError

Check warning on line 35 in core/api/src/app/payments/add-earn.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Errror" should be "Error".
}
| ApplicationError
> => {
const accountId = checkedToAccountId(accountIdRaw)
if (accountId instanceof Error) return accountId

Expand All @@ -53,12 +59,25 @@ export const addEarn = async ({
const user = await UsersRepository().findById(recipientAccount.kratosUserId)
if (user instanceof Error) return user

const isFirstTimeAnsweringQuestion =
await RewardsRepository(accountId).add(quizQuestionId)
if (isFirstTimeAnsweringQuestion instanceof Error) return isFirstTimeAnsweringQuestion

const quizQuestion: Quiz = {
id: quizQuestionId,
amount: amount,
completed: true,
}

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

if (validatedPhoneMetadata instanceof Error) {
return new InvalidPhoneForRewardError(validatedPhoneMetadata.name)
return {
quizQuestion,
rewardPaymentErrror: new InvalidPhoneForRewardError(validatedPhoneMetadata.name),

Check warning on line 79 in core/api/src/app/payments/add-earn.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Errror" should be "Error".
}
}

const accountIP = await AccountsIpsRepository().findLastByAccountId(recipientAccount.id)
Expand All @@ -67,26 +86,43 @@ export const addEarn = async ({
const validatedIPMetadata = IPMetadataAuthorizer(
rewardsConfig.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 {
quizQuestion,
rewardPaymentErrror: new InvalidIpMetadataError(validatedIPMetadata),

Check warning on line 94 in core/api/src/app/payments/add-earn.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Errror" should be "Error".
}

if (validatedIPMetadata instanceof UnauthorizedIPError)
return {
quizQuestion,
rewardPaymentErrror: validatedIPMetadata,

Check warning on line 100 in core/api/src/app/payments/add-earn.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Errror" should be "Error".
}

return {
quizQuestion,
rewardPaymentErrror: new UnknownRepositoryError("add earn error"),

Check warning on line 105 in core/api/src/app/payments/add-earn.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Errror" should be "Error".
}
}

const recipientWallets = await WalletsRepository().listByAccountId(accountId)
if (recipientWallets instanceof Error) return recipientWallets
if (recipientWallets instanceof Error)
return {
quizQuestion,
rewardPaymentErrror: recipientWallets,

Check warning on line 113 in core/api/src/app/payments/add-earn.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Errror" should be "Error".
}

const recipientBtcWallet = recipientWallets.find(
(wallet) => wallet.currency === WalletCurrency.Btc,
)
if (recipientBtcWallet === undefined) return new NoBtcWalletExistsForAccountError()
const recipientWalletId = recipientBtcWallet.id
if (recipientBtcWallet === undefined)
return {
quizQuestion,
rewardPaymentErrror: new NoBtcWalletExistsForAccountError(),

Check warning on line 122 in core/api/src/app/payments/add-earn.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Errror" should be "Error".
}

const shouldGiveReward = await RewardsRepository(accountId).add(quizQuestionId)
if (shouldGiveReward instanceof Error) return shouldGiveReward
const recipientWalletId = recipientBtcWallet.id

const payment = await intraledgerPaymentSendWalletIdForBtcWallet({
senderWalletId: funderWalletId,
Expand All @@ -95,7 +131,42 @@ export const addEarn = async ({
memo: quizQuestionId,
senderAccount: funderAccount,
})
if (payment instanceof Error) return payment

return { id: quizQuestionId, earnAmount: amount }
return {
quizQuestion,
rewardPaymentErrror: payment instanceof Error ? payment : undefined,

Check warning on line 137 in core/api/src/app/payments/add-earn.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Errror" should be "Error".
}
}

export const isAccountEligibleForEarnPayment = async ({
accountId,
}: {
accountId: AccountId
}): Promise<boolean | ApplicationError> => {
const recipientAccount = await AccountsRepository().findById(accountId)
if (recipientAccount instanceof Error) return recipientAccount

const user = await UsersRepository().findById(recipientAccount.kratosUserId)
if (user instanceof Error) return user

const rewardsConfig = getRewardsConfig()

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

if (validatedPhoneMetadata instanceof Error) {
return false
}

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

const validatedIPMetadata = IPMetadataAuthorizer(
rewardsConfig.ipMetadataValidationSettings,
).authorize(accountIP.metadata)

if (validatedIPMetadata instanceof Error) return false

return true
}
16 changes: 7 additions & 9 deletions core/api/src/graphql/public/root/mutation/quiz-completed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,19 @@ const QuizCompletedMutation = GT.Field<
resolve: async (_, args, { domainAccount }) => {
const { id } = args.input

const question = await Payments.addEarn({
const res = await Payments.addEarn({
quizQuestionId: id,
accountId: domainAccount.id,
})
if (question instanceof Error) {
return { errors: [mapAndParseErrorForGqlResponse(question)] }
if (res instanceof Error) {
return { errors: [mapAndParseErrorForGqlResponse(res)] }
}

return {
errors: [],
quiz: {
id: question.id,
amount: question.earnAmount,
completed: true,
},
errors: res.rewardPaymentErrror

Check warning on line 38 in core/api/src/graphql/public/root/mutation/quiz-completed.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Errror" should be "Error".
? [mapAndParseErrorForGqlResponse(res.rewardPaymentErrror)]

Check warning on line 39 in core/api/src/graphql/public/root/mutation/quiz-completed.ts

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"Errror" should be "Error".
: [],
quiz: res.quizQuestion,
}
},
})
Expand Down
1 change: 1 addition & 0 deletions core/api/src/graphql/public/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ type ConsumerAccount implements Account {

"""List the quiz questions of the consumer account"""
quiz: [Quiz!]!
quizRewardsEnabled: Boolean!
realtimePrice: RealtimePrice!

"""
Expand Down
16 changes: 15 additions & 1 deletion core/api/src/graphql/public/types/object/consumer-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import CallbackEndpoint from "./callback-endpoint"

import { NotificationSettings } from "./notification-settings"

import { Accounts, Prices, Wallets } from "@/app"
import { Accounts, Payments, Prices, Wallets } from "@/app"

import {
majorToMinorUnit,
Expand Down Expand Up @@ -158,6 +158,20 @@ const ConsumerAccount = GT.Object<Account, GraphQLPublicContextAuth>({
resolve: (source) => source.quiz,
},

quizRewardsEnabled: {
type: GT.NonNull(GT.Boolean),
resolve: async (source) => {
const rewardsEnabled = await Payments.isAccountEligibleForEarnPayment({
accountId: source.id,
})

if (rewardsEnabled instanceof Error) {
throw mapError(rewardsEnabled)
}

return rewardsEnabled
},
},
transactions: {
description:
"A list of all transactions associated with walletIds optionally passed.",
Expand Down

0 comments on commit 2da4245

Please sign in to comment.