During testing my Graph Project I receive the following error:
nft-lottery
Player entity
𝖷 (assert.fieldEquals) Expected field 'lotteries' to equal '[0x1]', but was '[]' instead.
🛠 Mapping aborted at ~lib/matchstick-as/assembly/assert.ts, line 13, column 7, with message: Assertion Error
wasm backtrace:
0: 0x387c - <unknown>!~lib/matchstick-as/assembly/assert/assert.fieldEquals
1: 0x38fa - <unknown>!start:tests/nft-lottery.test~anonymous|0~anonymous|3
If I log the results after calling handleNftLotteryEnter() function I get (i.e., the lottery.id has not been stored even though it should have as it is derivedFrom the NftLottery - see below):
"Player": {
"0xdb...a7bf": {
"lotteries": {
"type": "List",
"data": []
}
}
},
...
If I try to mock firing three subsequent events, the first two players has got values stored but the last one has not, i.e.:
"Player": {
"0x5a...3ca1": {
"lotteries": {
"type": "List",
"data": [
{
"type": "String",
"data": "0x1"
}
]
}
},
"0xd1...6575": {
"lotteries": {
"type": "List",
"data": []
}
},
"0xdb...a7bf": {
"lotteries": {
"type": "List",
"data": [
{
"type": "String",
"data": "0x1"
}
]
}
}
},
What could be the reason thereof?
Thank you in advance!
Test file
import {
assert,
describe,
test,
clearStore,
beforeAll,
afterAll,
logStore,
} from "matchstick-as/assembly/index"
import { BigInt, Address } from "#graphprotocol/graph-ts"
import { handleNftLotteryEnter } from "../src/nft-lottery"
import { createNftLotteryEnterEvent } from "./nft-lottery-utils"
describe("Enter the NFT Lottery 1x", () => {
beforeAll(() => {
let lotteryCounter = BigInt.fromI32(1)
let player_one = Address.fromString("0xDB...a7bf")
let entranceFee = BigInt.fromI32(1)
let newNftLotteryEnterEvent = createNftLotteryEnterEvent(
lotteryCounter,
player_one,
entranceFee
)
handleNftLotteryEnter(newNftLotteryEnterEvent)
logStore()
})
afterAll(() => {
clearStore()
})
test("Player entity", () => {
assert.fieldEquals("Player", "0xdb...a7bf", "lotteries", "[0x1]")
})
})
describe("Enter the NFT Lottery 3x", () => {
beforeAll(() => {
let lotteryCounter = BigInt.fromI32(1)
let entranceFee = BigInt.fromI32(1)
let player_one = Address.fromString("0xDB...a7bf")
let player_two = Address.fromString("0x5a...3cA1")
let player_three = Address.fromString("0xd1...6575")
// the first event
let oneNftLotteryEnterEvent = createNftLotteryEnterEvent(
lotteryCounter,
player_one,
entranceFee
)
handleNftLotteryEnter(oneNftLotteryEnterEvent)
// the second event
let twoNftLotteryEnterEvent = createNftLotteryEnterEvent(
lotteryCounter,
player_two,
entranceFee
)
// the third event
handleNftLotteryEnter(twoNftLotteryEnterEvent)
let threeNftLotteryEnterEvent = createNftLotteryEnterEvent(
lotteryCounter,
player_three,
entranceFee
)
handleNftLotteryEnter(threeNftLotteryEnterEvent)
logStore()
})
afterAll(() => {
clearStore()
})
test("Counts", () => {
assert.entityCount("NftLottery", 1)
assert.entityCount("Player", 3)
})
test("Player Entity 3x", () => {
assert.fieldEquals("Player", "0xdb...a7bf", "lotteries", "[0x1]")
})
})
Schema.graphql
type NftLottery #entity {
id: ID!
openAtTimestamp: BigInt!
closeAtTimestamp: BigInt!
prize: BigInt!
players: [Player!]!
winners: [Player!]
requestId: BigInt
updatedAtTimestamp: BigInt
}
type Player #entity {
id: ID! # address
lotteries: [NftLottery!]! #derivedFrom(field: "players")
}
# events
type NftLotteryEnter #entity {
id: ID! # Set lotteryCounter + playerAddress
lotteryCounter: BigInt!
player: Bytes!
numberOfEntrances: [BigInt!]!
updatedAtTimestamp: BigInt
}
Handlers
import { BigInt, Address } from "#graphprotocol/graph-ts"
import {
NftLotteryEnter as NftLotteryEnterEvent
} from "../generated/NftLottery/NftLottery"
import { NftLottery, Player, NftLotteryEnter } from "../generated/schema"
export function handleNftLotteryEnter(event: NftLotteryEnterEvent): void {
/* Event params */
const playerId = event.params.player.toHexString()
const lotteryId = event.params.lotteryCounter.toHexString()
const entranceFee = event.params.entranceFee.toString()
const lotteryPlayerId = lotteryId + playerId
/* NftLottery: if a lottery does not exist, create it */
let nftLottery = NftLottery.load(lotteryId)
if (!nftLottery) {
nftLottery = new NftLottery(lotteryId)
nftLottery.openAtTimestamp = event.block.timestamp
nftLottery.closeAtTimestamp = BigInt.fromString("000000000000")
nftLottery.requestId = BigInt.fromString("0")
nftLottery.prize = BigInt.fromString("0")
nftLottery.players = new Array<string>()
nftLottery.winners = new Array<string>()
}
// update lottery data
let currentPrize = nftLottery.prize
nftLottery.prize = currentPrize.plus(BigInt.fromString(entranceFee))
// update players
let arrPlayers = nftLottery.players
arrPlayers.push(playerId)
nftLottery.players = arrPlayers
nftLottery.updatedAtTimestamp = event.block.timestamp
nftLottery.save()
/* Player: if a player does not exist, create them */
let player = Player.load(playerId)
if (!player) {
player = new Player(playerId)
player.save()
}
/* NftLotteryEnter */
let nftLotteryEnter = NftLotteryEnter.load((lotteryPlayerId))
if (!nftLotteryEnter) {
nftLotteryEnter = new NftLotteryEnter(lotteryPlayerId)
nftLotteryEnter.lotteryCounter = BigInt.fromString(event.params.lotteryCounter.toString())
nftLotteryEnter.player = Address.fromHexString(playerId)
nftLotteryEnter.numberOfEntrances = BigInt.fromString("0")
}
// update entrances
const entrances = nftLotteryEnter.numberOfEntrances
nftLotteryEnter.numberOfEntrances = entrances.plus(BigInt.fromString("1"))
nftLotteryEnter.save()
}
There might be a bug in Matchstick testing tool:
As PlayerEntity derives lotteries from NftLottery, the NftLottery should be saved AT THE END of all operations related to both entities.
Otherwise Player.lotteries does not get updated immediately and it stays empty, thus test fails.
...
// update when updated
nftLottery.updatedAtTimestamp = event.block.timestamp
/* Player: if a player does not exist, create them */
let player = Player.load(playerId)
if (!player) {
player = new Player(playerId)
player.save()
}
nftLottery.save()
...
Credit goes to Maks#3349 at unit testing of Graph discord.
Related
I decided to use an OutlinedTextField and DropdownMenu so the user can fill an amount and select a currency.
This looks pretty nice in the preview, but when this code is being run on the device (virtual or physical) the DropdownMenu is being squeezed on the right, and therefore, the dropdown menu isn't actionable anymore.
#Composable
fun Money() {
Row() {
Amount()
Currency()
}
}
#Preview
#Composable
fun Currency() {
var mExpanded by remember { mutableStateOf(false) }
val mCurrencies = listOf("USD", "CHF", "EUR", "MAD") //, "Hyderabad", "Bengaluru", "PUNE")
var mSelectedText by remember { mutableStateOf("") }
var mTextFieldSize by remember { mutableStateOf(Size.Zero) }
val icon = if (mExpanded)
Icons.Filled.KeyboardArrowUp
else
Icons.Filled.KeyboardArrowDown
OutlinedTextField(
value = mSelectedText,
onValueChange = { mSelectedText = it },
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
// This value is used to assign to
// the DropDown the same width
mTextFieldSize = coordinates.size.toSize()
},
label = { Text("Currency") },
trailingIcon = {
Icon(icon, "contentDescription",
Modifier.clickable { mExpanded = !mExpanded })
}
)
DropdownMenu(
expanded = mExpanded,
onDismissRequest = { mExpanded = false },
modifier = Modifier
.width(with(LocalDensity.current) { mTextFieldSize.width.toDp() })
) {
mCurrencies.forEach { label ->
DropdownMenuItem(onClick = {
mSelectedText = label
mExpanded = false
}) {
Text(text = label)
}
}
}
}
#Composable
fun Amount() {
var amount by remember {
mutableStateOf("")
}
OutlinedTextField(
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
value = amount,
onValueChange = { amount = it },
label = { Text(text = "Amount") },
singleLine = true,
trailingIcon = {
if (amount.isNotBlank())
IconButton(onClick = { amount = "" }) {
Icon(
imageVector = Icons.Filled.Clear,
contentDescription = ""
)
}
}
)
}```
In the code written below, I am trying to push values in the array finalArray inside 'then'. After that, I need to return the value of finalArray present at point 1 to the front end. How can I achieve this? Putting return statement at point 2 doesn't help. It returns an empty array as expected.
Please help.
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: #loopback/example-file-transfer
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
import {inject} from '#loopback/core';
import {
repository
} from '#loopback/repository';
import {
get,
HttpErrors,
oas,
param,
Response,
RestBindings
} from '#loopback/rest';
import fs from 'fs';
import path from 'path';
import {promisify} from 'util';
import {STORAGE_DIRECTORY} from '../keys';
import {DocStoreRepository, MetadataCommonRepository} from '../repositories';
const readdir = promisify(fs.readdir);
/**
* A controller to handle file downloads using multipart/form-data media type
*/
export class FileDownloadController {
constructor(#inject(STORAGE_DIRECTORY) private storageDirectory: string,
#repository(MetadataCommonRepository) public metadataCommonRepository: MetadataCommonRepository,
#repository(DocStoreRepository) public docStoreRepository: DocStoreRepository,
) { }
finalArray: any = [];
// finalArray1: any = [];
// ObjofFinalArray = {}
#get('/files', {
responses: {
200: {
content: {
// string[]
'application/json': {
schema: {
type: 'array',
items: {
type: 'string',
},
},
},
},
description: 'A list of files',
},
},
})
async listFiles() {
const files = await readdir(this.storageDirectory)
var docNameArray = [];
docNameArray = files;
docNameArray.forEach((x, i) => {
//console.log("DocName", x, () => {});
var ObjofFinalArray = {
fileName: "",
DrawingNo: "",
AltNo: "",
SheetNo: ""
};
ObjofFinalArray.fileName = x;
this.docStoreRepository.find({where: {and: [{validFlag: true}, {useFlag: true}, {dsFileName: x}]}}).then((docstoreDataReturned) => {
// console.log("docstoreDataReturned", docstoreDataReturned);
return docstoreDataReturned;
}).then((docstoreDataReturned) => {
this.metadataCommonRepository.find({where: {and: [{validFlag: true}, {useFlag: true}, {mcDocstoreDk: docstoreDataReturned[0].dk}]}}).then((metadataCommonDataReturned) => {
//console.log("metadataCommonData", metadataCommonDataReturned)
ObjofFinalArray.DrawingNo = metadataCommonDataReturned[0].mcDrawNo;
ObjofFinalArray.AltNo = metadataCommonDataReturned[0].mcAltNo;
ObjofFinalArray.SheetNo = metadataCommonDataReturned[0].mcSheetNo;
return ObjofFinalArray;
}).then((ObjofFinalArr) => {
this.finalArray.push(ObjofFinalArr)
}).then(() => {
// console.log("this.finalArray", this.finalArray);
if (this.finalArray.length == files.length) {
console.log("this.finalArray", this.finalArray); /* point 1*/
}
});
})
});
return this.finalArray; /*point 2*/
// return files
}
I have this object in my dynamodb table which looks like
{
"id": "b31de483"
}
I want to add another object that looks like
{
"id": "b31de483",
"players": [{"playerId": "1234"}]
}
This is my code
const addPlayerToGame = async (gameId, playerId) => {
const params = {
TableName: process.env.DYNAMODB_GAMES_TABLE,
Key: {
id: gameId
},
UpdateExpression: 'set players = list_append(if_not_exists(players, :players)',
ExpressionAttributeValues: {
':players': {
"L": [
{ "S": playerId }
]
}
}
};
return await documentClient.update(params);
}
This throws an error but I cannot understand how to fix it. I am looking at documentation here
Figured it out
const addPlayerToGame = async (gameId, playerId) => {
const params = {
TableName: process.env.DYNAMODB_GAMES_TABLE,
Key: {
id: gameId
},
UpdateExpression: 'set players = list_append(players, :players)',
ExpressionAttributeValues: {
':players': [{
"playerId": playerId
}]
}
};
return await documentClient.update(params).promise();
}
Hi Guys I'm trying to filter post with data json format field?
"categoryList": ["cat", "cat1"]
For anyone still looking for a solution, this is what I have done for a json type field called tags of a collection type called Articles.
I have two articles in the database with one article having the following values set:
title: "lorem ipsum 1",
tags: [
"test",
"rest"
]
The other article has the following values set:
title: "lorem ipsum 2",
tags: [
"test",
"graphql"
]
My graphql query looks like this:
query {
articlesByTag(limit: 2, where: {tags_include: ["test", "rest"]}, start: 0, sort: "title:asc") {
title,
tags
}
}
While my rest query looks like this:
http://localhost:1337/articlesByTag?limit=2&tags_include[]=test&tags_include[]=rest
This is my articles.js service file:
const { convertRestQueryParams, buildQuery } = require('strapi-utils');
const _ = require('lodash');
const { convertToParams, convertToQuery } = require('../../../node_modules/strapi-plugin-graphql/services/utils');
module.exports = {
async findByTag(ctx) {
let tags_include;
if (ctx.where && ctx.where.tags_include && ctx.where.tags_include.length > 0) {
tags_include = ctx.where.tags_include;
delete ctx.where.tags_include;
} else if (ctx.query && ctx.query.tags_include && ctx.query.tags_include.length > 0) {
tags_include = ctx.query.tags_include;
delete ctx.query.tags_include;
}
if (!Array.isArray(tags_include)) {
tags_include = [tags_include];
}
let filters = null;
if (ctx.query) {
filters = convertRestQueryParams({
...convertToParams(ctx.query)
});
} else {
filters = convertRestQueryParams({
...convertToParams(_.pick(ctx, ['limit', 'start', 'sort'])),
...convertToQuery(ctx.where),
});
}
const entities = await strapi.query('articles').model.query(qb => {
buildQuery({ model: strapi.query('articles').model, filters: filters })(qb);
if (tags_include.length > 0) {
tags_include.forEach((tag) => {
if (tag && tag.length > 0) {
const likeStr = `%"${tag}"%`;
qb.andWhere('tags', 'like', likeStr);
}
});
}
}).fetchAll();
return entities;
},
};
This is the entry needed in routes.js
{
"method": "GET",
"path": "/articlesByTag",
"handler": "articles.findByTag",
"config": {
"policies": []
}
}
This is the controller articles.js
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
async findByTag(ctx) {
const entities = await strapi.services.articles.findByTag(ctx);
return entities.map(entity => sanitizeEntity(entity, { model: strapi.models.articles }));
},
};
And finally this is the schema.graphql.js
module.exports = {
query: `
articlesByTag(sort: String, limit: Int, start: Int, where: JSON): [Articles]
`,
resolver: {
Query: {
articlesByTag: {
description: 'Return articles filtered by tag',
resolverOf: 'application::articles.articles.findByTag',
resolver: async (obj, options, ctx) => {
return await strapi.api.articles.controllers.articles.findByTag(options);
},
},
},
},
};
There is not currently a way to filter the JSON fields yet as of beta.17.8 (latest)
Probably something like that?
strapi.query('cool_model').find({ categoryList: { $all: [ "cat" , "cat1" ] } })
Assume I have these two observations:
const thread = of({
thread: {
name: "Name",
author: null
}
})
const author = of({name:"Snoob"})
How can i get the merged result of these observations:
const threadWithAuthor = .....;
threadWithAuthor.subscribe(it=>console.log(it))
// {
// thread: {
// name: "Name",
// author: { name: "Snoob" }
// }
// }
Here's an example of how you can do it using combineLatest, pipe, and map:
var {of, combineLatest } = require('rxjs')
var { map } = require('rxjs/operators')
var mergeByAuthor = ([t, a]) => {
var x = Object.assign({}, t)
x.thread.author = a
return x
}
var thread = of({
thread: {
name: 'Name',
author: null
}
})
var author = of({name:'Snoob'})
var threadWithAuthor = combineLatest(thread, author).pipe(
map(mergeByAuthor)
)
threadWithAuthor.subscribe(x => console.log(JSON.stringify(x, null, 2)))
Output
{
"thread": {
"name": "Name",
"author": {
"name": "Snoob"
}
}
}