How to get unique smart contract errors in a frontend app using polkadot.js - substrate

I am implementing a smart contract with ink!
I have defined my own errors in the smart contract like the example below.
I don't know how to get the error information in my frontend app using polkadot.js when this error occurs.
Can anyone tell me who knows?
smart contract sample:
-- snip --
#[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub enum OwnErrors {
/// The Token Does Not Exists.
OwnErrorIsOccured,
}
-- snip --
#[ink(message)]
pub fn own_error_test(&mut self, account_id:AccountId, token_type:u8) -> OwnResult<()> {
if self.value == false {
return Err(OwnErrors::OwnErrorIsOccured);
}
self.token_list_for_id.insert(&self.next_id, &TokenInfo{token_address:account_id,token_type:TokenType::GovernanceToken});
self.next_id = self.next_id + 1;
Ok(())
}
-- snip --
frontend sample:
-- snip --
const own_error_test = async () => {
const { web3FromSource } = await import("#polkadot/extension-dapp");
const contract = new ContractPromise(api, abi, contractAddress);
const performingAccount = accounts[0];
const injector = await web3FromSource(performingAccount.meta.source);
const flip = await contract.tx.ownErrorTest(
{ value: 0, gasLimit: gasLimit },
actingAddress,
0
);
if (injector !== undefined) {
flip.signAndSend(actingAddress, { signer: injector.signer }, (result) => {
if (result.status.isInBlock) {
setResult("in a block");
} else if (result.status.isFinalized) {
setResult("finalized");
}
console.log("###result: ",result);
});
}
};
-- snip --

I got the answer on Astar Network's Discord.
https://substrate.stackexchange.com/questions/4247/how-to-get-output-when-calling-a-contract-method-and-signing-it/4261#4261
pub enum OwnErrors {
/// The Token Does Not Exists.
OwnErrorIsOccured,
}
if ( output?.toHuman()?.Err == "OwnErrorIsOccured"){
alert("I can handle errors");
return;
}

Related

Providing two combined Reducers for my redux saga store prevents my websocket channel message from triggering, but only one does not?

Configured my store this way with redux toolkit for sure
const rootReducer = combineReducers({
someReducer,
systemsConfigs
});
const store = return configureStore({
devTools: true,
reducer: rootReducer ,
// middleware: [middleware, logger],
middleware: (getDefaultMiddleware) => getDefaultMiddleware({ thunk: false }).concat(middleware),
});
middleware.run(sagaRoot)
And thats my channel i am connecting to it
export function createSocketChannel(
productId: ProductId,
pair: string,
createSocket = () => new WebSocket('wss://somewebsocket')
) {
return eventChannel<SocketEvent>((emitter) => {
const socket_OrderBook = createSocket();
socket_OrderBook.addEventListener('open', () => {
emitter({
type: 'connection-established',
payload: true,
});
socket_OrderBook.send(
`subscribe-asdqwe`
);
});
socket_OrderBook.addEventListener('message', (event) => {
if (event.data?.includes('bids')) {
emitter({
type: 'message',
payload: JSON.parse(event.data),
});
//
}
});
socket_OrderBook.addEventListener('close', (event: any) => {
emitter(new SocketClosedByServer());
});
return () => {
if (socket_OrderBook.readyState === WebSocket.OPEN) {
socket_OrderBook.send(
`unsubscribe-order-book-${pair}`
);
}
if (socket_OrderBook.readyState === WebSocket.OPEN || socket_OrderBook.readyState === WebSocket.CONNECTING) {
socket_OrderBook.close();
}
};
}, buffers.expanding<SocketEvent>());
}
And here's how my saga connecting handlers looks like
export function* handleConnectingSocket(ctx: SagaContext) {
try {
const productId = yield select((state: State) => state.productId);
const requested_pair = yield select((state: State) => state.requested_pair);
if (ctx.socketChannel === null) {
ctx.socketChannel = yield call(createSocketChannel, productId, requested_pair);
}
//
const message: SocketEvent = yield take(ctx.socketChannel!);
if (message.type !== 'connection-established') {
throw new SocketUnexpectedResponseError();
}
yield put(connectedSocket());
} catch (error: any) {
reportError(error);
yield put(
disconnectedSocket({
reason: SocketStateReasons.BAD_CONNECTION,
})
);
}
}
export function* handleConnectedSocket(ctx: SagaContext) {
try {
while (true) {
if (ctx.socketChannel === null) {
break;
}
const events = yield flush(ctx.socketChannel);
const startedExecutingAt = performance.now();
if (Array.isArray(events)) {
const deltas = events.reduce(
(patch, event) => {
if (event.type === 'message') {
patch.bids.push(...event.payload.data?.bids);
patch.asks.push(...event.payload.data?.asks);
//
}
//
return patch;
},
{ bids: [], asks: [] } as SocketMessage
);
if (deltas.bids.length || deltas.asks.length) {
yield putResolve(receivedDeltas(deltas));
}
}
yield call(delayNextDispatch, startedExecutingAt);
}
} catch (error: any) {
reportError(error);
yield put(
disconnectedSocket({
reason: SocketStateReasons.UNKNOWN,
})
);
}
}
After Debugging I got the following:
The Thing is that when I Provide one Reducer to my store the channel works well and data is fetched where as when providing combinedReducers I am getting
an established connection from my handleConnectingSocket generator function
and an empty event array [] from
const events = yield flush(ctx.socketChannel) written in handleConnectedSocket
Tried to clarify as much as possible
ok so I start refactoring my typescript by changing the types, then saw all the places that break, there was a problem in my sagas.tsx.
Ping me if someone faced such an issue in the future

Solana CPI Invocation Error. Error: Error processing Instruction 0: Cross-program invocation with unauthorised signer or writable account

I have basically converted a Crowd Funding Campaign written in Native Rust (Solana CLI) into an Anchor Version.
So my tests are running fine except the donation test.
Please if some Solana super shadowy dev can help me figure this out, I shall be grateful. I have spent almost a week debugging, but unable to figure out. Obviously somewhere my logic or syntax is wrong, but I am unable to understand.
Honestly, I feel my logic is right, because I have read the code so many times. But surely, something is wrong......
lib.rs
use anchor_lang::solana_program::system_program;
use anchor_lang::solana_program::program::{invoke /* , invoke_signed */};
use anchor_lang::solana_program::system_instruction;
use anchor_lang::solana_program::program_error::ProgramError;
declare_id!("HwHzMftiyTEYy6w8K6E3AipuF3vgswS1d7c6T6hUeRxf");
#[program]
pub mod solanacrowdfundingproject {
use super::*;
// `seeds` and `bump` tell us that our `writing_account` is a PDA that can be derived from their respective values
pub fn initialize(ctx: Context<Initialize>, writing_account_bump: u8) -> ProgramResult {
let writing_account = &mut ctx.accounts.writing_account;
let authority = &mut ctx.accounts.authority;
writing_account.bump = writing_account_bump;
writing_account.count = 0;
writing_account.authority = *authority.key;
writing_account.campaign_details = Vec::new();
writing_account.withdraw_request = Vec::new();
Ok(())
}
pub fn create_campaign
(
ctx : Context<CreateCampaign>,
name : String,
description : String,
image_link: String,
)
-> ProgramResult {
let writing_account = &mut ctx.accounts.writing_account;
let authority = &mut ctx.accounts.authority;
if name.len() > 30 || description.len() > 50 {
return Err(ErrorCode::NameOrDescriptionTooLong.into())
}
let campaign_data = CampaignDetails {
admin : *authority.key,
name : name.to_string(),
description : description.to_string(),
image_link : image_link.to_string(),
amount_donated : 0,
};
writing_account.count += 1;
writing_account.campaign_details.push(campaign_data);
Ok(())
}
pub fn withdraw(ctx : Context<Withdraw>, amount : u64) -> ProgramResult {
let writing_account = &mut ctx.accounts.writing_account;
let authority = &mut ctx.accounts.authority;
let withdraw_data = WithdrawRequest {
amount_withdrawn : amount,
admin : *authority.to_account_info().key,
};
writing_account.withdraw_request.push(withdraw_data);
**writing_account.to_account_info().try_borrow_mut_lamports()? -= amount;
**authority.to_account_info().try_borrow_mut_lamports()? += amount;
Ok(())
}
pub fn donate(ctx : Context<Donate>, amount : u64, donator_program_account_bump : u8) -> ProgramResult {
let writing_account = &mut ctx.accounts.writing_account;
let donator_program_account = &mut ctx.accounts.donator_program_account;
let authority = &mut ctx.accounts.authority;
donator_program_account.amount_donated = amount;
donator_program_account.bump = donator_program_account_bump;
let transfer_ix = system_instruction::transfer(
&authority.to_account_info().key(),
&donator_program_account.to_account_info().key(),
amount,
);
#[warn(unused_must_use)]
invoke(
&transfer_ix,
&[
authority.to_account_info(),
donator_program_account.to_account_info(),
],
)?;
let mut campaign_data = CampaignDetails::try_from_slice(*writing_account.to_account_info().try_borrow_mut_data()?)
.expect("Error deserializing data");
campaign_data.amount_donated += **donator_program_account.to_account_info().lamports.borrow();
**writing_account.to_account_info().try_borrow_mut_lamports()? += **donator_program_account.to_account_info().lamports.borrow();
**donator_program_account.to_account_info().try_borrow_mut_lamports()? = 0;
*donator_program_account.to_account_info().try_borrow_mut_data()? = &mut [];
Ok(())
}
#[derive(Accounts)]
#[instruction(writing_account_bump : u8)]
pub struct Initialize<'info> {
#[account(init,
seeds = [b"please_____".as_ref(), authority.key().as_ref()],
bump = writing_account_bump,
payer = authority,
space = 9000)]
pub writing_account : Account<'info, CampaignState>,
#[account(mut)]
pub authority : Signer<'info>,
#[account(address = system_program::ID)]
pub system_program : AccountInfo<'info>,
}
#[derive(Accounts)]
pub struct CreateCampaign<'info> {
#[account(mut, has_one = authority)]
pub writing_account : Account<'info, CampaignState>,
#[account(mut)]
pub authority : Signer<'info>,
}
#[derive(Accounts)]
pub struct Withdraw<'info> {
#[account(mut, has_one = authority)]
pub writing_account : Account<'info, CampaignState>,
#[account(mut)]
pub authority : Signer<'info>,
}
#[derive(Accounts)]
#[instruction(donator_program_account_bump : u8)]
pub struct Donate<'info> {
#[account(mut)]
pub writing_account : Account<'info, CampaignState>,
#[account(mut)]
pub authority : Signer<'info>,
#[account(init,
seeds = [b"donate____".as_ref(),
authority.key().as_ref()],
bump = donator_program_account_bump,
payer = authority,
space = 100)]
pub donator_program_account : Account<'info, DonatorProgramAccount>,
#[account(address = system_program::ID)]
pub system_program : AccountInfo<'info>,
}
#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
pub struct CampaignDetails {
pub admin: Pubkey,
pub name: String,
pub description: String,
pub image_link: String,
pub amount_donated: u64,
}
#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)]
pub struct WithdrawRequest {
pub amount_withdrawn : u64,
pub admin : Pubkey,
}
#[account]
pub struct CampaignState {
pub campaign_details : Vec<CampaignDetails>,
pub bump : u8,
pub count : u8,
pub authority: Pubkey,
pub withdraw_request : Vec<WithdrawRequest>,
}
#[account]
pub struct DonatorProgramAccount {
pub amount_donated : u64,
bump : u8,
}
#[error]
pub enum ErrorCode {
#[msg("Name cannot be more than 30 characters and Description cannot be more than 50 characters")]
NameOrDescriptionTooLong,
}
}
also my tests as below
const anchor = require('#project-serum/anchor');
const { PublicKey, Connection } = require("#solana/web3.js");
//const cluster = "http://localhost:8899";
const cluster = "https://api.devnet.solana.com";
const connection = new Connection(cluster, "confirmed");
const { SystemProgram, Keypair, /*SYSVAR_RENT_PUBKEY*/ } = anchor.web3;
const { Buffer } = require('buffer');
// Specify provider environment.
const provider = anchor.Provider.env();
//Set provider.
anchor.setProvider(provider);
//Specify the workspace
const program = anchor.workspace.Solanacrowdfundingproject;
//const programID = await connection.programID(program);
const programID = new PublicKey("HwHzMftiyTEYy6w8K6E3AipuF3vgswS1d7c6T6hUeRxf");
// const otherUser (i.e Donator)
const donator = Keypair.generate();
describe('Solanacrowdfundingproject', () => {
console.log("🚀 Starting tests...");
try {
it('gets initialized', async () => {
const { writingAccount, bump } = await getProgramDerivedCampaignWritingAccountAddress();
let tx = await program.rpc.initialize(new anchor.BN(bump), {
accounts: {
writingAccount: writingAccount,
authority: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
});
//Console.log the Transaction signature of the Initialization procedure.
console.log("Initialization transaction signature : ", tx);
//Asserts and console.logs
const account = await program.account.campaignState.fetch(writingAccount);
console.log("👀 Created A Writing Account : ", account);
assert.equal(account.authority.toBase58(), provider.wallet.publicKey.toBase58());
console.log("👀 Account's count is :", account.count);
});
} catch (error) {
console.log(error);
}
try {
it('Creates a campaign', async () => {
const { writingAccount } = await getProgramDerivedCampaignWritingAccountAddress();
//Lets invocate the createCampaign function using provider.wallet.publicKey
let tx = await program.rpc.createCampaign("Suveett", "Blockchain Speaker", "Enter a fancy giflink for Campaign",
{
accounts: {
writingAccount: writingAccount,
authority: provider.wallet.publicKey,
},
});
//Asserts and Console Logs
//Console.log the Transaction signature of the Initialization procedure.
console.log("Your CreateCampaign transaction signature", tx);
let account = await program.account.campaignState.fetch(writingAccount);
console.log("Writing Account after Campaign Creation :", account);
console.log("This Writing account's address is : ", writingAccount.toBase58());
console.log("This Writing account's admin is : ", account.campaignDetails[0].admin.toBase58());
console.log("This Writing account's Campaign Details contains `name` :", account.campaignDetails[0].name);
console.log("This Writing account's Campaign Details contains `description` :", account.campaignDetails[0].description);
assert.equal(account.authority.toBase58(), provider.wallet.publicKey.toBase58());
});
} catch (error) {
console.log(error);
}
try {
it('Can Make a Donation', async () => {
const signature = await connection.requestAirdrop(donator.publicKey, 1000000000);
await connection.confirmTransaction(signature);
console.log("Airdrop confirmed :", await connection.getBalance(donator.publicKey));
const { writingAccount } = await getProgramDerivedCampaignWritingAccountAddress();
const { donatorProgramAccount, bump } = await getProgramDerivedDonatorProgramAccountAddress();
let balanceOfCampaignCreatorPreDonation = await connection.getBalance(writingAccount);
console.log("Balance of Campaign before Donation : ", balanceOfCampaignCreatorPreDonation);
let donateTx = await program.rpc.donate(new anchor.BN(1000000), new anchor.BN(bump),
{
accounts: {
writingAccount: writingAccount,
authority: donator.publicKey,
donatorProgramAccount: donatorProgramAccount,
systemProgram: SystemProgram.programId,
},
signers: [donator],
});
//Asserts and Console Logs
//Console.log the Transaction signature of the Donation procedure.
let account = await program.account.donatorProgramAccount.fetch(donatorProgramAccount);
console.log("👀 Created a New Donator Program Account : ", account);
console.log("👀 Your Donation transaction signature is : ", donateTx);
let balanceOfCampaignCreatorPostDonation = await connection.getBalance(writingAccount);
console.log("Balance of Campaign after Donation : ", balanceOfCampaignCreatorPostDonation);
});
} catch (error) {
console.log(error);
}
try {
it('Can Make a Withdrawal', async () => {
const { writingAccount } = await getProgramDerivedCampaignWritingAccountAddress();
let withdrawTx = await program.rpc.withdraw(new anchor.BN(50000),
{
accounts: {
writingAccount: writingAccount,
authority: provider.wallet.publicKey,
}
});
//Asserts and Console Logs
//Console.log the Transaction signature of the Withdrawal procedure.
console.log("Your Withdrawal transaction signature", withdrawTx);
let balanceOfCampaignCreator = await connection.getBalance(writingAccount);
console.log("Balance of Campaign after Withdrawal: ", balanceOfCampaignCreator);
});
} catch (error) {
console.log(error);
}
});
async function getProgramDerivedCampaignWritingAccountAddress() {
const [writingAccount, bump] = await PublicKey.findProgramAddress(
[Buffer.from('please_____'), provider.wallet.publicKey.toBuffer()],
programID
);
console.log(`Got ProgramDerivedWritingAccountAddress: bump: ${bump}, pubkey: ${writingAccount.toBase58()}`);
return { writingAccount, bump };
};
async function getProgramDerivedDonatorProgramAccountAddress() {
const [donatorProgramAccount, bump] = await PublicKey.findProgramAddress(
[Buffer.from('donate____'), donator.publicKey.toBuffer()],
programID
);
console.log(`Got ProgramDerivedDonatorProgramAccountAddress: bump: ${bump}, pubkey: ${donatorProgramAccount.toBase58()}`);
return { donatorProgramAccount, bump };
};
Can someone go through the code and help me?
Also, the line Code below (where I am getting the error):
https://github.com/Suveett/solana-crowd-funding-master-using-Anchor/blob/ad87fb2226c800fce1faabd355eb88b812441e0c/tests/solanacrowdfundingproject.js#L91
Try to add mut in your declaration:
#[account(init, mut,seeds = [b"please_____".as_ref(), authority.key().as_ref()],bump = writing_account_bump, payer = authority,space = 9000)]

How to use SSR with Redux in Next.js(Typescript) using next-redux-wrapper? [duplicate]

This question already has an answer here:
next-redux-wrapper TypeError: nextCallback is not a function error in wrapper.getServerSideProps
(1 answer)
Closed 1 year ago.
Using redux with SSR in Next.js(Typescript) using next-redux-wrapper, but getting error on this line
async ({ req, store })
Says, Type 'Promise' provides no match for the signature '(context: GetServerSidePropsContext<ParsedUrlQuery, PreviewData>): Promise<GetServerSidePropsResult<{ [key: string]: any; }>>
Property 'req' does not exist on type 'Store<EmptyObject & { filterReducer: never; }, any> & { dispatch: unknown; }'.
Property 'store' does not exist on type 'Store<EmptyObject & { filterReducer: never; }, any> & { dispatch: unknown; }'
Here is my SSR code:-
export const getServerSideProps: GetServerSideProps = wrapper.getServerSideProps(async ({ req, store }) => {
let { query } = req
let searchCategory = query.category?.toString().toLowerCase().replace(/ /g, "-");
const apolloClient = initializeApollo();
const response = await apolloClient.query({
query: GET_PRODUCT_BY_CATEGORY,
variables: {
numProducts: 10,
category: searchCategory
}
});
await store.dispatch(getProducts(response));
});
You're calling wrapper.getServerSideProps in a wrong way.
Try like the following:
export const getServerSideProps = wrapper.getServerSideProps(
store => async ({req, res, query}) => {
// do your stuff with store and req
}
);
If you're looking for a working demo, you can visit my old answer
This code base could help you. ("next": "10.1.3")
Try using getInitialProps instead of getServerSideProps.
This works in my case. Like code below:
Try
in _app.js
import { wrapper } from '/store';
function MyApp(props) {
const { Component, pageProps } = props;
...
return (
<Component {...pageProps} />
)
}
App.getInitialProps = async props => {
const { Component, ctx } = props;
const pageProps = Component.getInitialProps
? await Component.getInitialProps(ctx)
: {};
//Anything returned here can be accessed by the client
return { pageProps: pageProps, store: ctx.store };
};
export default wrapper.withRedux(App);
store.js file:
const makeStore = props => {
if (!isEmpty(props)) {
return createStore(reducer, bindMiddleware([thunkMiddleware]));
} else {
const { persistStore, persistReducer } = require('redux-persist');
const persistConfig = {
key: 'root',
};
const persistedReducer = persistReducer(persistConfig, reducer); // Create a new reducer with our existing reducer
const store = createStore(
persistedReducer,
bindMiddleware([thunkMiddleware])
); // Creating the store again
store.__persistor = persistStore(store); // This creates a persistor object & push that persisted object to .__persistor, so that we can avail the persistability feature
return store;
}
};
// Export the wrapper & wrap the pages/_app.js with this wrapper only
export const wrapper = createWrapper(makeStore);
in your page:
HomePage.getInitialProps = async ctx => {
const { store, query, res } = ctx;
};

Firebase Function Returns Before All Callback functions complete execution

I'm using the Google Storage NodeJS client library to list GCS Bucket paths.
Here's the code to the Firebase Function:
import * as functions from 'firebase-functions';
import { Storage } from '#google-cloud/storage';
import { globVars } from '../admin/admin';
const projectId = process.env.GCLOUD_PROJECT;
// shared global variables setup
const { keyFilename } = globVars;
// Storage set up
const storage = new Storage({
projectId,
keyFilename,
});
export const gcsListPath = functions
.region('europe-west2')
.runWith({ timeoutSeconds: 540, memory: '256MB' })
.https.onCall(async (data, context) => {
if (context.auth?.token.email_verified) {
const { bucketName, prefix, pathList = false, fileList = false } = data;
let list;
const options = {
autoPaginate: false,
delimiter: '',
prefix,
};
if (pathList) {
options.delimiter = '/';
let test: any[] = [];
const callback = (_err: any, _files: any, nextQuery: any, apiResponse: any) => {
test = test.concat(apiResponse.prefixes);
console.log('test : ', test);
console.log('nextQuery : ', nextQuery);
if (nextQuery) {
storage.bucket(bucketName).getFiles(nextQuery, callback);
} else {
// prefixes = The finished array of prefixes.
list = test;
}
}
storage.bucket(bucketName).getFiles(options, callback);
}
if (fileList) {
const [files] = await storage
.bucket(bucketName)
.getFiles(options);
list = files.map((file) => file.name);
}
return { list }; //returning null as it exec before callback fns finish
} else {
return {
error: { message: 'Bad Request', status: 'INVALID_ARGUMENT' },
};
}
});
My problem is that my Firebase function returns the list (null) before all the callback functions finish execution.
Could someone spot and point out what needs to be changed/added to make the function wait for all the callback functions to finish. I've tried adding async/await but can't seem to get it right.
The reason for your error is that you use a callback. It's not awaited in the code. I would recommend to turn the callback code to a promise. Something like this.
import * as functions from "firebase-functions";
import { Storage } from "#google-cloud/storage";
import { globVars } from "../admin/admin";
const projectId = process.env.GCLOUD_PROJECT;
// shared global variables setup
const { keyFilename } = globVars;
// Storage set up
const storage = new Storage({
projectId,
keyFilename,
});
const getList = (bucketName, options) => {
return new Promise((resolve, reject) => {
let list;
let test: any[] = [];
const callback = (
_err: any,
_files: any,
nextQuery: any,
apiResponse: any
) => {
test = test.concat(apiResponse.prefixes);
console.log("test : ", test);
console.log("nextQuery : ", nextQuery);
if (nextQuery) {
storage.bucket(bucketName).getFiles(nextQuery, callback);
} else {
// prefixes = The finished array of prefixes.
list = test;
}
resolve(list);
};
try {
storage.bucket(bucketName).getFiles(options, callback);
} catch (error) {
reject(eror);
}
});
};
export const gcsListPath = functions
.region("europe-west2")
.runWith({ timeoutSeconds: 540, memory: "256MB" })
.https.onCall(async (data, context) => {
if (context.auth?.token.email_verified) {
const { bucketName, prefix, pathList = false, fileList = false } = data;
let list;
const options = {
autoPaginate: false,
delimiter: "",
prefix,
};
if (pathList) {
options.delimiter = "/";
list = await getList(bucketName, options);
}
if (fileList) {
const [files] = await storage.bucket(bucketName).getFiles(options);
list = files.map((file) => file.name);
}
return { list }; //returning null as it exec before callback fns finish
} else {
return {
error: { message: "Bad Request", status: "INVALID_ARGUMENT" },
};
}
});
I'm not sure if the part with fileList will work as expectedt. It looks like the API doesn't support await but only callbacks.
import * as functions from "firebase-functions";
import { GetFilesOptions, Storage } from "#google-cloud/storage";
import { globVars } from "../admin/admin";
const projectId = process.env.GCLOUD_PROJECT;
// shared global variables setup
const { keyFilename } = globVars;
// Storage set up
const storage = new Storage({
projectId,
keyFilename,
});
const getList = (bucketName: string, options: GetFilesOptions) => {
return new Promise((resolve, reject) => {
// let test: any[] = [];
let list: any[] = [];
const callback = (
_err: any,
_files: any,
nextQuery: any,
apiResponse: any
) => {
list = list.concat(apiResponse.prefixes);
console.log("list : ", list);
console.log("nextQuery : ", nextQuery);
if (nextQuery) {
storage.bucket(bucketName).getFiles(nextQuery, callback);
} else {
// prefixes = The finished array of prefixes.
resolve(list);
}
};
try {
storage.bucket(bucketName).getFiles(options, callback);
} catch (error) {
reject(error);
}
});
};
export const gcsListPath = functions
.region("europe-west2")
.runWith({ timeoutSeconds: 540, memory: "256MB" })
.https.onCall(async (data, context) => {
if (context.auth?.token.email_verified) {
const { bucketName, prefix, pathList = false, fileList = false } = data;
let list;
const options = {
autoPaginate: false,
delimiter: "",
prefix,
};
if (pathList) {
options.delimiter = "/";
list = await getList(bucketName, options);
}
if (fileList) {
const [files] = await storage.bucket(bucketName).getFiles(options);
list = files.map((file) => file.name);
}
return { list }; //returning null as it exec before callback fns finish
} else {
return {
error: { message: "Bad Request", status: "INVALID_ARGUMENT" },
};
}
});

How to Unit Test Graphql Resolver functions created using apollo-resolvers

I have created resolvers(userresolver.js) using 'apollo-resolvers' npm module as below.
import { createResolver } from 'apollo-resolvers';
import { isInstance } from 'apollo-errors';
const baseResolver = createResolver(
null,
(root, args, context, error) => isInstance(error) ? error : new UnknownError()
);
const users = baseResolver.createResolver(
(parent, args, { models, me } ) => {
return Object.values(models.users);
}
);
export default {
Query: {
users
}
}
;
These also work fine when I test the queries after starting the server.
I now want to do unit testing of the resolver functions.
I am not sure how to do that. Can someone help me on how to unit test the resolver functions. I am using mocha with chai for testing my project.
You can try easygraphql-tester, it has a method that'll help you to test the resolvers.
Here is the documentation of it.
Example:
Resolver
"use strict";
const license = (__, args, ctx) => {
const { key } = args;
return {
id: "1234",
body: "This is a test license",
description: `This is a description with key ${key}`
};
};
module.exports = {
Query: {
license
}
};
Test
"use strict";
const fs = require("fs");
const path = require("path");
const { expect } = require("chai");
const EasyGraphQLTester = require("easygraphql-tester");
const resolvers = require("../resolvers");
const schemaCode = fs.readFileSync(
path.join(__dirname, "..", "schema.gql"),
"utf8"
);
describe("Test resolvers", () => {
let tester;
beforeAll(() => {
tester = new EasyGraphQLTester(schemaCode, resolvers);
});
it("should return expected values", async () => {
const query = `
query GET_LICENSE($key: String!) {
license(key: $key) {
id
body
description
}
}
`;
const args = {
key: "1234"
};
const result = await tester.graphql(query, {}, {}, args);
expect(result.data.license.id).to.be.eq("1234");
expect(result.data.license.body).to.be.eq("This is a test license");
expect(result.data.license.description).to.be.eq(
`This is a description with key ${args.key}`
);
});
});

Resources