Neo4j/GraphQL augmented Schema - neo4j

I'm trying to learn a bit about Neo4j's GraphQL integration. I'm using the GrandStack starter which can be found here. Grand Stack Starter. The starter doesn't use a lot of the boilerplate that you see in other GrapQL applications because, as I understand it, the Neo4j integration is supposed to bypass the need to double declare Schema and Resolvers. It uses Apollo Server and an "augmentedSchema" instead. Using the starter code I've tried to add some mutations to the schema, in this case, a deleteUser mutation. I keep getting an error, saying that the Mutation id defined more than once, when I know I'm only putting it in the code in one place. This is my schema.js:
import { neo4jgraphql } from "neo4j-graphql-js";
export const typeDefs = `
type User {
id: ID!
name: String
friends(first: Int = 10, offset: Int = 0): [User] #relation(name: "FRIENDS", direction: "BOTH")
reviews(first: Int = 10, offset: Int = 0): [Review] #relation(name: "WROTE", direction: "OUT")
avgStars: Float #cypher(statement: "MATCH (this)-[:WROTE]->(r:Review) RETURN toFloat(avg(r.stars))")
numReviews: Int #cypher(statement: "MATCH (this)-[:WROTE]->(r:Review) RETURN COUNT(r)")
}
type Business {
id: ID!
name: String
address: String
city: String
state: String
reviews(first: Int = 10, offset: Int = 0): [Review] #relation(name: "REVIEWS", direction: "IN")
categories(first: Int = 10, offset: Int =0): [Category] #relation(name: "IN_CATEGORY", direction: "OUT")
}
type Review {
id: ID!
stars: Int
text: String
business: Business #relation(name: "REVIEWS", direction: "OUT")
user: User #relation(name: "WROTE", direction: "IN")
}
type Category {
name: ID!
businesses(first: Int = 10, offset: Int = 0): [Business] #relation(name: "IN_CATEGORY", direction: "IN")
}
type Query {
users(id: ID, name: String, first: Int = 10, offset: Int = 0): [User]
businesses(id: ID, name: String, first: Int = 10, offset: Int = 0): [Business]
reviews(id: ID, stars: Int, first: Int = 10, offset: Int = 0): [Review]
category(name: ID!): Category
usersBySubstring(substring: String, first: Int = 10, offset: Int = 0): [User] #cypher(statement: "MATCH (u:User) WHERE u.name CONTAINS $substring RETURN u")
}
type Mutation {
deleteUser(id: ID!): User
}
`;
export const resolvers = {
Query: {
users: neo4jgraphql,
businesses: neo4jgraphql,
reviews: neo4jgraphql,
category: neo4jgraphql,
usersBySubstring: neo4jgraphql
},
Mutation: {
deleteUser: neo4jgraphql
}
};
Here's my index:
import { typeDefs, resolvers } from "./graphql-schema";
import { ApolloServer, gql, makeExecutableSchema } from "apollo-server";
import { v1 as neo4j } from "neo4j-driver";
import { augmentSchema } from "neo4j-graphql-js";
import dotenv from "dotenv";
dotenv.config();
const schema = makeExecutableSchema({
typeDefs,
resolvers
});
// augmentSchema will add autogenerated mutations based on types in schema
const augmentedSchema = augmentSchema(schema);
const driver = neo4j.driver(
process.env.NEO4J_URI || "bolt://localhost:7687",
neo4j.auth.basic(
process.env.NEO4J_USER || "neo4j",
process.env.NEO4J_PASSWORD || "neo4j"
)
);
if (driver){
console.log("Database Connected")
} else{
console.log("Database Connection Error")
}
const server = new ApolloServer({
// using augmentedSchema (executable GraphQLSchemaObject) instead of typeDefs and resolvers
//typeDefs,
//resolvers,
context: { driver },
// remove schema and uncomment typeDefs and resolvers above to use original (unaugmented) schema
schema: augmentedSchema
});
server.listen(process.env.GRAPHQL_LISTEN_PORT, '0.0.0.0').then(({ url }) => {
console.log(`GraphQL API ready at ${url}`);
});
Error Message:
No other code from the starter has been changed. I haven't been able to find much info on the augmentedSchema in the docs. If anyone can point me in the right direction I would appreciate it.

The issue was caused by the augmentedSchema, which automatically creates default mutations for each type i.e. in this case it had already created delete user, update user, and add user. You can see this when you run the GraphiQL server. So I was double declaring the delete user mutation.

Related

failed to send transaction: Cross-program invocation with unauthorized signer or writable account

I am trying to build a web3 based e-commerce site using Anchor.
I've just started learning about PDAs and there's a error I've been getting for hours, like the one in the title.
My contract:
#[program]
pub mod dailsap_store_contract {
use super::*;
pub fn create_collection(
ctx: Context<CreateCollection>,
name: String,
description: String,
image_uri: String,
) -> Result<()> {
let collection: &mut Account<Collection> = &mut ctx.accounts.collection;
let authority: &Signer = &ctx.accounts.authority;
let clock: Clock = Clock::get().unwrap();
let bump = *ctx.bumps.get("collection").unwrap();
if name.chars().count() > 50 {
return Err(ErrorCode::CollectionNameTooLong.into());
}
if description.chars().count() > 250 {
return Err(ErrorCode::CollectionDescriptionTooLong.into());
}
if image_uri.chars().count() > 60 {
return Err(ErrorCode::CollectionImageUrlTooLong.into());
}
collection.authority = *authority.key;
collection.timestamp = clock.unix_timestamp;
collection.name = name;
collection.description = description;
collection.image = image_uri;
collection.bump = bump;
Ok(())
}
pub fn update_collection(
ctx: Context<UpdateCollection>,
name: String,
description: String,
image_uri: String,
) -> Result<()> {
let base_collection: &mut Account<Collection> = &mut ctx.accounts.collection_account;
base_collection.name = name;
base_collection.description = description;
base_collection.image = image_uri;
Ok(())
}
}
#[derive(Accounts)]
pub struct CreateCollection<'info> {
#[account(mut)]
pub authority: Signer<'info>,
#[account(init, payer=authority, space = Collection::LEN, seeds=[b"collection", collection.key().as_ref()], bump)]
pub collection: Account<'info, Collection>,
#[account(address = system_program::ID)]
pub system_program: Program<'info, System>,
}
#[account]
pub struct Collection {
authority: Pubkey,
timestamp: i64,
name: String,
description: String,
image: String,
bump: u8,
}
Frontend:
const collection = anchor.web3.Keypair.generate();
const [collectionPDA, _] = await anchor.web3.PublicKey.findProgramAddress(
[
anchor.utils.bytes.utf8.encode("collection"),
collection.publicKey.toBuffer(),
],
program.programId
);
await program.methods
.createCollection(
"This is collection name",
"This is collection description",
"Hello World"
)
.accounts({
collection: collectionPDA,
authority: anchor.AnchorProvider.env().publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
The problem should be here: seeds=[b"collection", collection.key().as_ref()]
The source from which I received help: https://book.anchor-lang.com/anchor_in_depth/PDAs.html
But I'm getting errors in a way I don't understand
can you help?
To be honest, I'm surprised the program got so far! The program-derived address is meant to be derived from a set of seeds and the program id, and in your case, the collection address is using itself as a seed, which would normally mean infinite recursion. But in this case, it only goes down one level and fails to derive itself.
Try any other seeds on your collection address, even something like:
#[account(init, payer=authority, space = Collection::LEN, seeds=[b"collection", authority.key().as_ref()], bump)]
pub collection: Account<'info, Collection>,
I came up with a solution like this, I'm not sure how logical it is.
...
#[program]
pub mod dailsap_store_contract {
use super::*;
pub fn create_collection(
ctx: Context<CreateCollection>,
id: Pubkey,
name: String,
description: String,
image_uri: String,
) -> Result<()> {
let collection: &mut Account<Collection> = &mut ctx.accounts.collection;
let authority: &Signer = &ctx.accounts.authority;
let clock: Clock = Clock::get().unwrap();
let bump = *ctx.bumps.get("collection").unwrap();
if name.chars().count() > 50 {
return Err(ErrorCode::CollectionNameTooLong.into());
}
...
#[derive(Accounts)]
#[instruction(id: Pubkey)]
pub struct CreateCollection<'info> {
#[account(mut)]
pub authority: Signer<'info>,
#[account(init, payer=authority, space = Collection::LEN, seeds=[b"collection", id.as_ref()], bump)]
pub collection: Account<'info, Collection>,
#[account(address = system_program::ID)]
pub system_program: Program<'info, System>,
}
#[account]
pub struct Collection {
id: Pubkey,
authority: Pubkey,
timestamp: i64,
name: String,
description: String,
image: String,
bump: u8,
}
...
Frontend:
const collection = anchor.web3.Keypair.generate();
const [collectionPDA, _] = await anchor.web3.PublicKey.findProgramAddress(
[
Buffer.from(anchor.utils.bytes.utf8.encode("collection")),
collection.publicKey.toBuffer(),
],
program.programId
);
await program.methods
.createCollection(
collection.publicKey,
"This is collection name",
"This is collection description",
"Hello World"
)
.accounts({
collection: collectionPDA,
authority: anchor.AnchorProvider.env().publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();

Classifier 'Context' does not have a companion object, and thus must be initialized here

Here is my code
fun Uri.getOriginalFileName(context: Context): String? {
return context.contentResolver.query(this, null, null, null, null)?.use {
val nameColumnIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
it.moveToFirst()
it.getString(nameColumnIndex)
}
}
#Composable
fun AppNotice() {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*"; ///
intent.addCategory(Intent.CATEGORY_OPENABLE);
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
activityResult -> if (activityResult.resultCode == RESULT_OK)
{
Log.d("appDebug", "oky")
var uri = intent.getData()
val name: String? = uri?.getOriginalFileName(Context)
Log.d("seeFile", "$name")
}
else {
Log.d("appDebug", "Denied")
}
Button(onClick={launcher.launch(intent)})
{Text("Button")}
}
Context is a class then how to know what method will i need in this line/context name: String? = uri?.getOriginalFileName(Context)
I went to adroid.dev page but found a huge page and cant figure out what i need since saw only few uri permission related stuffs.

How to send a GET request with an array as a parameter?

I was trying to create a function to make a GET with query parameters. I was dealing with the Mangadex API and was to send a parameter called 'manga' as an array. I created the code as follows:
Future<http.Response> getCoverArtResponse(String mangaID) async {
var queryParameters = {
'limit': '10',
'manga': [mangaID] //Here
};
var unencodedPath = '/cover';
var response = await http.get(
Uri.https(authority, unencodedPath, queryParameters),
headers: {HttpHeaders.contentTypeHeader: 'application/json'});
return response;
}
However, the response was the following error:
{"result":"error","errors":[{"id":"9c346772-7b14-5982-b4b6-7b5888522762","status":400,"title":"validation_exception","detail":"Error validating \/manga: String value found, but an array is required","context":null}]}
How am I supposed to send the parameters? So far I have tried -
'manga': [mangaID]
'manga': '[$mangaID]'
None of them seem to work.
import 'dart:async';
import 'package:wnetworking/wnetworking.dart';
class MangaDex {
static const _base = 'https://api.mangadex.org';
static FutureOr<void> _getter({required String url, required Function(JMap item, int idx) onItem}) async {
await HttpReqService.getJson<JMap>(url)
.then((response) {
var results = response?['results'];
if (results != null) {
if (results is List) {
var i = 0;
results.forEach((manga) => onItem(manga, ++i));
} else {
print(response);
}
}
});
}
static FutureOr<void> cover({int limit = 10, int offset=0, String? mangaId, String? coverId}) async {
final mangas = mangaId != null ? '&manga[]=$mangaId' : '';
final covers = coverId != null ? '&ids[]=$coverId' : '';
final url = '$_base/cover?limit=$limit&offset=$offset$mangas$covers';
await _getter(
url: url,
onItem: (item, idx) {
print('$idx) "${item['data']?['attributes']?['fileName']}"');
print(' id: ${item['data']?['id']}\n');
},
);
}
}
void main(List<String> args) async {
await MangaDex.cover(mangaId: '32d76d19-8a05-4db0-9fc2-e0b0648fe9d0', limit: 2);
print('\nJob done');
}
Result:
1) "f5873770-80a4-470e-a11c-63b709d87eb3.jpg"
id: b6c7ce9c-e671-4f26-90b0-e592188e9cd6
2) "e9f926db-b469-48c4-8cc4-a8e523ad75ca.jpg"
id: 00aae6e0-46bb-4f92-a82a-1c740789b704
Job done
Replace wnetworking package with http package, and JMap with Map<String, dynamic>
NOTE: MangaDex Documentation is lacking and misleading about how to correctly use its endpoints.

converting string to map in dart

I wanted to convert a string to map.
String value = "{first_name : fname,last_name : lname,gender : male, location : { state : state, country : country, place : place} }"
into
Map = {
first_name : fname,
last_name : lname,
gender : male,
location = {
state : state,
country : country,
place : place
}
}
How do I convert the string into a map<String, dynamic> where the value consists of string, int, object, and boolean?
I wanted to save the string to a file and obtain the data from the file.
That's not possible.
If you can change the string to valid JSON, you can use
import 'dart:convert';
...
Map valueMap = json.decode(value);
// or
Map valueMap = jsonDecode(value);
The string would need to look like
{"first_name" : "fname","last_name" : "lname","gender" : "male", "location" : { "state" : "state", "country" : "country", "place" : "place"} }
You would have to change the way you create the string.
I'm guessing you are creating the string using the yourMap.toString() method. You should rather use json.encode(yourMap), which converts your map to valid JSON, which you can the parse with json.decode(yourString).
create two objects
class User {
final String firstName;
final String lastName;
final String gender;
final location;
User({
this.firstName,
this.lastName,
this.gender,
this.location,
});
User.fromJson(Map json)
: firstName = json['firstName'],
lastName = json['lastName'],
gender = json['gender'],
location = Location.fromJson(json['location']);
}
class Location {
final String state;
final String country;
final String place;
Location({
this.state,
this.country,
this.place,
});
Location.fromJson(Map json)
: state = json['state'],
country = json['country'],
place = json['place'];
}
then use it like this
var user = User.fromJson(value);
print(user.firstName);
or convert it to list like this
var user = User.fromJson(value).toList();
you can do like this ->
import 'dart:convert';
...
if your data like this **
{'bus1':'100Tk','bus2':'150TK','bus3':'200TK'}
**;
then you can do like this ->
Map valueMap = json.decode(value);
// or
Map valueMap = jsonDecode(value);
or if like this ->var data = {'1':'100TK','2':'200TK','3':'300TK'};
var dataSp = data.split(',');
Map<String,String> mapData = Map();
dataSp.forEach((element) => mapData[element.split(':')[0]] = element.split(':')[1]);
Note: Map first value was Int that's why I did that.
Make a wrapper class for the location where you define the methods fromMap, toMap
Yeah, that's not possible.
But i have workaround to fix that.
Remove space in ur invalid json
Fix ur invalid string json to valid string json
Convert valid string json to map
Here's the full code for above process:
import 'dart:convert';
void main() {
String value = "{first_name : fname,last_name : lname,gender : male, location : { state : state, country : country, place : place} }";
String jsonString = _convertToJsonStringQuotes(raw: value);
print("Test 1: $jsonString");
final Map<dynamic, dynamic> result = json.decode(jsonString);
print('Test 2: $result');
}
String _convertToJsonStringQuotes({required String raw}) {
/// remove space
String jsonString = raw.replaceAll(" ", "");
/// add quotes to json string
jsonString = jsonString.replaceAll('{', '{"');
jsonString = jsonString.replaceAll(':', '": "');
jsonString = jsonString.replaceAll(',', '", "');
jsonString = jsonString.replaceAll('}', '"}');
/// remove quotes on object json string
jsonString = jsonString.replaceAll('"{"', '{"');
jsonString = jsonString.replaceAll('"}"', '"}');
/// remove quotes on array json string
jsonString = jsonString.replaceAll('"[{', '[{');
jsonString = jsonString.replaceAll('}]"', '}]');
return jsonString;
}
To convert a string into a map<String, dynamic>, you can use the
following code:
String value = "{first_name : fname,last_name : lname,gender : male, location : { state : state, country : country, place : place} }";
String result = value
.replaceAll("{","{\"")
.replaceAll("}","\"}")
.replaceAll(":","\":\"")
.replaceAll(",","\",\"");
print(result);
Here, we first replace the opening and closing curly braces with double quotes, and then replace the colons and commas with quotes to create a valid JSON string. Then, we use the jsonDecode method to convert the JSON string into a map.
I found a way to cast that string
Ok, lets use a complex model to cast:
final testMap = {
'userName': 'Igor',
'age': 22,
'totalCash': 138.57,
'isMale:': true,
'userStatus': {
'isUserActive': true,
'isAPremiumUser': false,
},
'userTags': ['Flutter Developer', 'Proactive', 'Clean code'],
'userCourses': [
{
'title': 'How to use TDD in flutter',
'finished': false,
'coursePercentage': 47.4,
'buyDate': '1969-07-20T20:18:04.000Z',
'courseTag': ['New', 'Popular'],
'courseDetails': null,
},
{
'title': 'Clean arquiteture in flutter',
'finished': false,
'coursePercentage': 20.8,
'buyDate': '1969-07-20T20:18:04.000Z',
'courseTag': ['New'],
'courseDetails': {
'teacherName': 'Tayler Mostoult',
'totalSubscribers': 5402,
},
},
{
'title': 'Micro-frontends in flutter',
'finished': true,
'coursePercentage': 100.0,
'buyDate': '1969-07-20T20:18:04.000Z',
'courseTag': [],
'courseDetails': {},
},
]
};
Know, cast it to string:
final testMapInStringFormat = testMap.toString();
To convert this String to map, we can use:
final String response = _getJsonFromString(testMap.toString());
final Map jsonConvertido = jsonDecode(response); // Decoded, back to map format
The function that will effectively do the casting:
String _getJsonFromString(String rawText) {
// Will find, for exemple, the text: "{isUserActive:"
final regexMapKeyWithOpenBracket = RegExp('(?<={)(.*?):+');
// Will find, for exemple, the text: ", userCourses:"
final regexMapKeyWithCommaAndSpace = RegExp(r'(?<=, )([^\]]*?):');
final regexOnlyKeyInLine = RegExp(r'^.+:$');
final splitedSentences = rawText
.replaceAllMapped(regexMapKeyWithCommaAndSpace,
(Match match) => '\n${match.text.trim()}\n')
.replaceAllMapped(regexMapKeyWithOpenBracket,
(Match match) => '\n${match.text.trim()}\n')
.replaceAll(RegExp(r'}(?=,|]|}|$|\s+)'), '\n}\n')
.replaceAll(RegExp(r'(?<=(,|:|^|\[)\s?){'), '\n{\n')
.replaceAll(RegExp('\\[\\s?\\]'), '\n[\n]\n')
.replaceAll(RegExp('\\{\\s?\\}'), '\n{\n}\n')
.split('\n')
..removeWhere((element) => element.replaceAll(' ', '').isEmpty);
final List<String> correctLines = [];
for (String line in splitedSentences) {
final isMapKey = regexOnlyKeyInLine.hasMatch(line);
if (isMapKey) {
final lineWithoutFinalTwoDots = line.substring(0, line.length - 1);
final lineWithQuaot = _putQuotationMarks(lineWithoutFinalTwoDots);
correctLines.add('$lineWithQuaot:');
} else {
String l = line.trim();
// If it falls in this else, it is a value of a key or a map structure
final isNumber = double.tryParse(l) != null || int.tryParse(l) != null;
final isBolean = l == 'false' || l == 'true';
final isStructureCaracter = ['{', '}', '[', ']', ','].any((e) => e == l);
final isNull = l == 'null';
if (isStructureCaracter || isNumber || isBolean || isNull) {
correctLines.add(l);
continue;
}
final hasCommaInFinal = l.endsWith(',');
if (hasCommaInFinal) {
l = l.substring(0, l.length - 1);
}
// If you got to this point, i'm sure it's a value string, so lets add a double quote
final lineWithQuaot = _putQuotationMarks(l);
if (hasCommaInFinal) {
correctLines.add('$lineWithQuaot,');
} else {
correctLines.add(lineWithQuaot);
}
}
}
return correctLines.join('');
}
extension MatchExtension on Match {
String get text => input.substring(start, end);
}
String _putQuotationMarks(String findedText) {
if (!findedText.startsWith('\'') && !findedText.startsWith('"')) {
findedText = findedText[0] + findedText.substring(1);
}
if (!findedText.endsWith('\'')) {
final lastIndex = findedText.length - 1;
findedText = findedText.substring(0, lastIndex) + findedText[lastIndex];
}
return '"$findedText"';
}
Use below method
just pass String json data it will give Map data
jsonStringToMap(String data){
List<String> str = data.replaceAll("{","").replaceAll("}","").replaceAll("\"","").replaceAll("'","").split(",");
Map<String,dynamic> result = {};
for(int i=0;i<str.length;i++){
List<String> s = str[i].split(":");
result.putIfAbsent(s[0].trim(), () => s[1].trim());
}
return result;
}

Clob field not populated in object in grailsChange changeset using grails DSL

I am making my first migration script that involves the use of the Groovy/Grails DSL. It looks like this:
import my.package.MyObject
import my.package.MyObjectUtil
import javax.xml.bind.DatatypeConverter
databaseChangeLog = {
changeSet(author: 'wheresjim', id: '1387238199-1') {
comment {'Sets the timestamp in each MyObject where null using the message text'}
grailsChange {
change {
MyObjectUtil myObjectUtil = new MyObjectUtil()
def criteria = MyObject.where {
isNull("timestamp")
}
def PAGESIZE = 10
int numRows = criteria.count()
int pages = Math.ceil(numRows / PAGESIZE)
(pages..0).each { page ->
int offset = PAGESIZE * page + PAGESIZE
def data = criteria.list(offset: offset, max: PAGESIZE, sort: 'id', order: 'asc')
data.each { MyObject myObject ->
Date timestamp = new Date(0L)
try {
def thisMessage = myObjectUtil.createMyObjectFromMessage(myObject.messageText)
String dateStr = thisMessage.messageIdentification?.timestamp
timestamp = dateStr ? DatatypeConverter.parseDateTime(dateStr).getTime() : timestamp
} catch (Exception e) {
// Do nothing, this will be logged in the finally which catches another error condition
} finally {
if (timestamp == new Date(0L)) {
log.warn "Error attempting to set timestamp in MyObject ${myObject.id}, setting to $eventDateTime"
}
}
myObject.timestamp = timestamp
myObject.save(flush: true)
}
log.warn "Updated ${myObject.id}"
}
}
}
}
}
The MyObject.messageText is a clob in the database, and to my knowledge, there has been no effort to lazily load it.
I should note that this exact script works (it can find the clob text) using the grails console plugin on the app.
In MyObject make sure your have the following lines:
static mapping = {
messageText type: 'text'
}

Resources