I've spent my entire Saturday trying to figure out why this isn't working
I've tried server things by now... Furthermore, I couldn't find anything online
this is what I have now:
schema GraphQL:
`type Organization #model #auth(rules: [{ allow: custom }]) {
id: ID!
organizationName: String!
organizationDescription: String
users: [User] #hasMany
owner: User #hasOne
branches: [Branch] #hasMany
}
type Branch #model #auth(rules: [{ allow: custom }]) {
id: ID!
branchName: String!
branchDescription: String
registrationNumber: String
organization: Organization #belongsTo
users: [User] #manyToMany(relationName: "BranchesUsers")
}
type User #model #auth(rules: [{ allow: custom }]) {
id: ID!
userType: UserTypeEnum!
userStatus: UserStatusEnum!
userEmail: String
userPhoneNumber: String
userFullName: String
branches: [Branch] #manyToMany(relationName: "BranchesUsers")
organization: Organization #belongsTo
}`
The code:
`Future<void> createAccount(Organization organization, Branch branch, User user) async {
Organization newOrganization = Organization(id: organization.id,
organizationName: "", branches: [branch], users: [user], owner: user);
Organization? createdOrganization = await _createOrganization(newOrganization);
if(createdOrganization == null) {
//TODO rollback
throw ApiException("ERROR");
}
Branch newBranch = Branch(id: branch.id, branchName: branch.branchName, organization: createdOrganization);
Branch? createdBranch = await _createBranch(newBranch);
if(createdBranch == null) {
//TODO rollback
throw ApiException("ERROR");
}
User newUser = User(id: user.id, userType: user.userType,
userStatus: user.userStatus, organization: createdOrganization);
User? createdUser = await _createUser(newUser);
if(createdUser == null) {
//TODO rollback
throw ApiException("ERROR");
}
await _createAssociationUserBranch(createdBranch, createdUser);
}`
`Future<Organization?> _createOrganization(Organization organization) async {
GraphQLRequest request = ModelMutations.create(organization);
final response = await Amplify.API.mutate(request: request).response;
final created = response.data;
if (created == null) {
safePrint('errors: ${response.errors}');
Amplify.Auth.signOut();
throw ApiException("Critical error");
}
return created;
}`
`Future<Branch?> _createBranch(Branch branch) async {
GraphQLRequest request = ModelMutations.create(branch);
final response = await Amplify.API.mutate(request: request).response;
final created = response.data;
if (created == null) {
safePrint('errors: ${response.errors}');
Amplify.Auth.signOut();
throw ApiException("Critical error");
}
return created;
}`
`Future<User?> _createUser(User user) async {
GraphQLRequest request = ModelMutations.create(user);
final response = await Amplify.API.mutate(request: request).response;
final created = response.data;
if (created == null) {
safePrint('errors: ${response.errors}');
Amplify.Auth.signOut();
throw ApiException("Critical error");
}
return created;
}`
`Future<void> _createAssociationUserBranch(Branch branch, User user) async {
BranchesUsers branchesUsers = BranchesUsers(user: user, branch: branch);
GraphQLRequest<BranchesUsers> branchUserRequest = ModelMutations.create(branchesUsers);
final branchUserResponse = await Amplify.API.mutate(request: branchUserRequest).response;
final branchUser = branchUserResponse.data;
if (branchUser == null) {
safePrint('errors: ${branchUserResponse.errors}');
Amplify.Auth.signOut();
throw ApiException("Critical error");
}
User updatedUser = User(id: user.id, userType: user.userType, userStatus: user.userStatus,
organization: user.organization, branches: [branchUser]);
Branch updatedBranch = Branch(id: branch.id, branchName: branch.branchName,
organization: branch.organization, users: [branchUser]);
GraphQLRequest<User> userRequest = ModelMutations.update(updatedUser);
GraphQLRequest<Branch> branchRequest = ModelMutations.update(updatedBranch);
Amplify.API.mutate(request: userRequest);
Amplify.API.mutate(request: branchRequest);
return null;
}`
if for some f up luck this is right, then I have no Idea how I should query this to fetch the many-to-many relationship :/
Related
I am using MongoDB. I can post put and delete also get, can patch using JsonPatchDocument but face issue when trying to update in db.
Last Methods in UserService and UserController, which I want make for patch using JsonPatchDocument
This is my appsetting.json file
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"HealthCareDatabase": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "HealthApp",
"UserCollectionName":"User",
"JobCollectionName":"Jobs",
"JobApplicationCollectionName":"jobApplications",
"EducationCollectionName":"Education",
"ExperienceCollectionName":"Experience",
"TestCollectionName":"Test"
}
}
UserServices.cs
using backend.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using Microsoft.AspNetCore.JsonPatch;
namespace backend.Services;
public class UserService :ControllerBase
{
private readonly IMongoCollection<User> _userCollection;
public UserService(
IOptions<HealthCareDbSettings> HealthCareDbSettings)
{
var mongoClient = new MongoClient(
HealthCareDbSettings.Value.ConnectionString);
var mongoDatabase = mongoClient.GetDatabase(
HealthCareDbSettings.Value.DatabaseName);
_userCollection = mongoDatabase.GetCollection<User>(
HealthCareDbSettings.Value.UserCollectionName);
}
// User Services are down there
public async Task<List<User>> GetAsync() =>
await _userCollection.Find(_ => true).ToListAsync();
public async Task<User?> GetAsync(string id) =>
await _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
public User Login(string email, string password)
{
var user = _userCollection.Find(user => user.Email == email ).FirstOrDefault();
bool isPasswordValid = BCrypt.Net.BCrypt.Verify(password,user.Password);
if (isPasswordValid)
{
return user;
}
else{
return null;
}
}
public async Task CreateUser(User newUser) =>
await _userCollection.InsertOneAsync(newUser);
public async Task UpdateUser(string id,User user) =>
await _userCollection.ReplaceOneAsync(user => user.Id == id,user);
// This method has error
public async Task<User> updateUser(string id, JsonPatchDocument<User> patchDoc) {
var fromDb = await _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
var filter = _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
patchDoc.ApplyTo(fromDb,ModelState);
_userCollection.UpdateOne(filter,fromDb);
var user = await _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
return user;
}
}
UserController.cs
using Microsoft.AspNetCore.Mvc;
using backend.Services;
using backend.Models;
using Microsoft.AspNetCore.JsonPatch;
namespace backend.Controllers;
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly UserService _userService;
public UserController(UserService userservice)
{
_userService = userservice;
}
public string err = "Invalid Credentials";
[HttpGet]
public async Task<List<User>> Get() =>
await _userService.GetAsync();
[HttpGet("{id:length(24)}")]
public async Task<ActionResult<User>> Get(string id)
{
var user = await _userService.GetAsync(id);
if(user is null){
return NotFound();
}
return user;
}
[HttpPost("Login")]
public User userLogin([FromBody] Login login)
{
User usr = _userService.Login(login.email,login.password);
if(usr != null){
return usr;
}else {
return null;
}
}
// Creating New User
[HttpPost]
public async Task<IActionResult> Post(User newUser)
{
var usr = newUser;
var hashed = BCrypt.Net.BCrypt.HashPassword(newUser.Password);
usr.Password = hashed;
await _userService.CreateUser(usr);
return CreatedAtAction(nameof(Get), new { id = usr.Id }, usr);
}
//Update User
// [HttpPatch("up/{id:length(24)}")]
// public Task<User> update(string id,[FromBody] JsonPatchDocument<User> patchDoc)
// {
// var usr = _userService.GetAsync(id);
// return _userService.updateUser(id,patchDoc);
// }
}
I am trying to follow this tutorial to establish integration tests on our web application. Our stack currently includes Nexus, Next, Apollo, Prisma, and PostgreSQL.
I am using ApolloClient in place of GraphQLClient from graphql-request, I opted to use ApolloClient instead, especially since our web application is server less.
This is currently what I have inside the helper.ts, and the ApolloClient does work when I execute mutations. However, after executing a mutation on ApolloClient and checking if the data persists through Prisma, I get a null value.
Did I do these adjustments correctly? I am definitely missing something if Prisma is not querying correctly. Maybe there is a disconnect here between ApolloClient and Prisma or ApolloClient and the database? Any help would be much appreciated.
All of the code is below.
helper.ts
function graphqlTestContext() {
let serverInstance: ServerInfo | null = null;
return {
async before() {
const rootUrl = getRootUrl();
const httpLink = createHttpLink({
uri: rootUrl + "api/graphql",
credentials: "include",
fetch
});
const client = new ApolloClient({
// ssrMode: typeof window === "undefined",
link: httpLink,
cache: new InMemoryCache(),
});
return client;
},
async after() {
serverInstance?.server.close()
},
}
}
function prismaTestContext() {
const prismaBinary = join(__dirname, '../../', 'node_modules', '.bin', 'prisma');
let schema = '';
let databaseUrl = '';
let prismaClient: null | PrismaClient = null;
return {
async before() {
// Generate a unique schema identifier for this test context
schema = `test_${nanoid()}`;
// Generate the pg connection string for the test schema
databaseUrl = `${process.env.ROOT_DB_URL}/testing?schema=${schema}`;
// Set the required environment variable to contain the connection string
// to our database test schema
process.env.DATABASE_URL = databaseUrl;
// Run the migrations to ensure our schema has the required structure
execSync(`${prismaBinary} migrate dev`, {
env: {
...process.env,
DATABASE_URL: databaseUrl,
},
});
// Construct a new Prisma Client connected to the generated Postgres schema
prismaClient = new PrismaClient();
return prismaClient;
},
async after() {
// Drop the schema after the tests have completed
const client = new Client({
connectionString: databaseUrl,
});
await client.connect();
await client.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`);
await client.end();
// Release the Prisma Client connection
await prismaClient?.$disconnect();
},
}
User.int.test.ts
const ctx = createTestContext();
describe("User", () => {
it("creates a new user with REGISTER_MUTATION", async () => {
const userResult = await ctx.client.mutate({
mutation: gql`
mutation Register(
$firstName: String!
$lastName: String!
$email: String!
$password: String!
) {
registerUser(
firstName: $firstName
lastName: $lastName
email: $email
password: $password
) {
user {
email
firstName
}
}
}
`,
variables: {
firstName: "FirstName",
lastName: "LastName",
email: "test#email.com",
password: "password"
}
});
expect(userResult).toMatchInlineSnapshot(`
Object {
"data": Object {
"registerUser": Object {
"__typename": "UserLoginPayload",
"user": Object {
"__typename": "User",
"email": "test#email.com",
"firstName": "FirstName",
},
},
},
}
`);
});
it("verifies that user persists", async () => {
const persistedData = await ctx.prisma.user.findMany();
expect(persistedData).toMatchInlineSnapshot(`Array []`);
});
});
The reason is because graphql server is instantiated with different prisma client with its own db. And the prismaTestContext has its own prisma client with different db url.
I'm using Nest.js with TypeORM and Postgres.
What I'm trying to do is create and instance of my User entity. It has Membership and File fields that are also entities. Those has One to One relationship with User, so are created when User is created. If anything goes wrong I want to rollback everything, therefore I'm using a transaction.
This is what I'm doing:
async create(userProcessed: UserProcessed): Promise<UserResponse> {
const response: UserResponse = {
message: 'User not created',
success: false,
user: undefined,
};
const queryRunner = this.connection.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const membership = new Membership();
membership.membershipState = userProcessed.membershipState;
membership.membershipType = userProcessed.membershipType;
const membershipSaved = await queryRunner.manager.save(membership);
if (!membershipSaved) throw Error();
const file = new File();
file.weight = userProcessed.file.weight;
file.height = userProcessed.file.height;
const fileSaved = await queryRunner.manager.save(file);
if (!fileSaved) throw Error(response.message);
const user = new User();
user.email = userProcessed.email;
user.firstName = userProcessed.firstName;
user.lastName = userProcessed.lastName;
user.gender = userProcessed.gender;
user.file = fileSaved;
user.membership = membershipSaved;
user.role = userProcessed.role;
user.birthday = new Date(userProcessed.birthday);
user.setPassword(userProcessed.password);
const userSaved = await this.userRepository.save(user);
console.log(userSaved);
if (!userSaved) throw Error(response.message);
response.success = true;
response.message = 'User created';
response.user = userSaved;
await queryRunner.commitTransaction();
return response;
} catch (e) {
await queryRunner.rollbackTransaction();
throw Error(response.message);
} finally {
await queryRunner.release();
}
}
The problem comes when user is created. As I'm using an transaction, nothing is saved into the DB until I call await queryRunner.commitTransaction(); (as it has to be). As result, the membership and file that I'm trying to use as foreign key doesn't really exists.
Is there a way that I can solve this in the same transaction?
It was simpler than I thought.
I just found that I can set { cascade: ['insert'] } in the #OneToOne annotation in User. With that, there is no need to save the File or Membership before User, are saved when User is saved.
In user.entity.ts:
...
#OneToOne(type => Membership, { cascade: ['insert'] })
#JoinColumn({ name: 'membership_id' })
membership: Membership;
#OneToOne(type => File, { cascade: ['insert'] })
#JoinColumn({ name: 'file_id' })
file: File;
...
In user.service.ts
async create(userProcessed: UserProcessed): Promise<UserResponse> {
const response: UserResponse = {
message: 'User not created',
success: false,
user: undefined,
};
const queryRunner = this.connection.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const membership = new Membership();
membership.membershipState = userProcessed.membershipState;
membership.membershipType = userProcessed.membershipType;
const file = new File();
file.weight = userProcessed.file.weight;
file.height = userProcessed.file.height;
const user = new User();
user.email = userProcessed.email;
user.firstName = userProcessed.firstName;
user.lastName = userProcessed.lastName;
user.gender = userProcessed.gender;
user.file = file;
user.membership = membership;
user.role = userProcessed.role;
user.birthday = new Date(userProcessed.birthday);
user.setPassword(userProcessed.password);
const userSaved = await this.userRepository.save(user);
console.log(userSaved);
if (!userSaved) throw Error(response.message);
response.success = true;
response.message = 'User created';
response.user = userSaved;
await queryRunner.commitTransaction();
return response;
} catch (e) {
await queryRunner.rollbackTransaction();
throw Error(response.message);
} finally {
await queryRunner.release();
}
}
And with that everything works as a charm!
I am trying to insert the object in FamilyData and same object in UserData in callback function of FamilyData.But here,I am able to insert an object to FamilyData but in callback function object I am getting is null.And it is causing asynchronous callback exception.In console ,I am getting upto "user id present" with stringified object but after that "FamilyId" is not printing.There it is showing null object.
The code I have attached below:
`
registerNewUser': function (obj) {
let result=null;
result = {};
let UserData1;
try {
let userId;
let messageArray;
let FamilyEmail;
let FamilyId1;
messageArray = Meteor.Validation.Registration(obj);
if (messageArray.length > 0) {
let msg = Meteor.Utility.appendMessages(messageArray);
result.Success = false;
result.Message = msg;
} else {
result.Success = true;
result.Message = 'User added successfully';
if (!obj.check_familymember) {
userId = Accounts.createUser({
email: obj.email,
password: obj.pwd
});
if (userId != undefined) {
console.log("user id present",JSON.stringify(obj));
FamilyData.insert({
FamilyName: obj.FamilyName,
Address: obj.Address,
CreatedBy: obj.firstname,
Latitude: obj.latitude,
Longitude: obj.longitude
}, function (err, FamilyId) {
console.log("FamilyId",JSON.stringify(obj));
UserData.insert({
FirstName: obj.firstname,
LastName: obj.lastname,
Gender: obj.genderval,
DateOfBirth: obj.dob,
Email: obj.email,
Password: obj.pwd,
FamilyId: FamilyId,
UserId: userId,
});
if(err){
// throw err;
result.Message=err;
console.log("error is",err);
}
});
console.log("user created");
} else {
result.Success = false;
result.Message = 'Unable to create user ';
}
} else {
FamilyEmail = obj.FamilyEmailId;
UserData1 = UserData.findOne({
Email: FamilyEmail
});
if (UserData1 == null) {
result.Success = false;
result.Message = 'FamilyId does not exist..';
} else {
FamilyId1 = UserData1.FamilyId;
userId = Accounts.createUser({
email: obj.email,
password: obj.pwd
});
if (userId != undefined) {
UserData.insert({
FirstName: obj.firstname,
LastName: obj.lastname,
Gender: obj.genderval,
DateOfBirth: obj.dob,
Email: obj.email,
Password: obj.pwd,
FamilyId: FamilyId1,
UserId: userId
});
} else {
result.Success = false;
result.Message = 'Unable to create user ';
}
}
}
}
} catch (e) {
logError(e);
result.Success = false;
result.Message = e.message;
}
`
Meteor uses fibers to run asynchronous code
so you don't havee to use callback in db operations
try the following
let familyId = FamilyData.insert({
FamilyName: obj.FamilyName,
Address: obj.Address,
CreatedBy: obj.firstname,
Latitude: obj.latitude,
Longitude: obj.longitude
});
I am try to make arr a array that every user has but is never sent to client side. A day ago the it stopped being put into user objects on user create. Here is the code; thanks.
client
Template.create_user.events({
'click #create-user-button': function() {
var username = $("#username").val();
var password = $("#password").val();
var email = $("#email").val();
var bio = $("#bio").val() || "";
if (!username || !password || !email) {
} else {
Accounts.createUser({
username: username,
password: password,
email: email,
arr:[],
profile: {
bio: bio
}
});
}
}
});
server/user.js
Accounts.onCreateUser(function(options, user) {
if (options.profile)
user.profile = options.profile;
return user;
});
Accounts.createUser takes an object with at most 4 fields: username, email, password, and profile. You are passing in arr, which is being ignored by the server. You have two options:
Put arr inside of the profile object.
Add arr to the user in the Accounts.onCreateUser callback.
option 1:
Accounts.createUser({
username: username,
password: password,
email: email,
profile: {
bio: bio,
arr: []
}
});
option 2:
Accounts.onCreateUser(function(options, user) {
if (options.profile)
user.profile = options.profile;
user.arr = [];
return user;
});
In that case, you will also need to publish the extra field so that the client can see it. See the users section of the docs. Specifically:
// server
Meteor.publish("userData", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId}, {fields: {arr: 1}});
} else {
this.ready();
}
});
// client
Meteor.subscribe("userData");