graphql-shield as 'makeProcessSchemaPlugin' in Postgraphile - postgraphile

Tried postgrphile example but not sure where mistake were done?
I am trying to implement graphql-shield as a plugin
middlewarePlugin = makeProcessSchemaPlugin((schema: typeof GraphQLSchema) => {
return applyMiddleware(schema, permissions);
});
permission.ts
const { rule, shield } = require("graphql-shield");
const isAuthenticated = rule()((parent: any, args: any, user: any ) => {
return user !== null;
});
const permissions = shield({
Query: {
viewer: isAuthenticated
}
});
export = permissions;
I import middlewarePlugin as others plugins in postgraphile:
appendPlugins: [
myClass.myPlugin,
myClass.jsonPlace,
myClass.middlewarePlugin
],
crash log:
| A serious error occurred when building the initial schema. Exiting because retryOnInitFail is not set. Error details:
graphql |
graphql | TypeError: Cannot read property 'fragment' of undefined
graphql | at isMiddlewareWithFragment (/home/node/app/node_modules/graphql-middleware/src/utils.ts:25:17)
graphql | at Object.isMiddlewareFunction (/home/node/app/node_modules/graphql-middleware/src/utils.ts:33:10)
graphql | at Object.validateMiddleware (/home/node/app/node_modules/graphql-middleware/src/validation.ts:9:7)
graphql | at addMiddlewareToSchema (/home/node/app/node_modules/graphql-middleware/src/middleware.ts:33:27)
graphql | at normalisedMiddlewares.reduceRight.schema.schema (/home/node/app/node_modules/graphql-middleware/src/middleware.ts:91:11)
graphql | at Array.reduceRight ()
graphql | at applyMiddlewareWithOptions (/home/node/app/node_modules/graphql-middleware/src/middleware.ts:80:77)
graphql | at applyMiddleware (/home/node/app/node_modules/graphql-middleware/src/middleware.ts:132:10)
graphql | at hook (/home/node/app/resolvers/test.resolver.ts:254:16)
graphql | at SchemaBuilder.applyHooks (/home/node/app/node_modules/graphile-build/src/SchemaBuilder.js:398:20)

The type of middleware postgraphile expects is a little different from graphql-middleware. Instead of downloading graphql-middleware and graphql-shield and trying to get them to work with postgraphile, I ended up writing my own "shield" with makeWrapResolversPlugin. Example:
const filter = (ctx) => {
// Only attempting to do auth for non-root things, because
// with postgraphile, all the data gets fetched at the root
if (ctx.scope.isRootMutation || ctx.scope.isRootQuery) {
// return this to make it available to the wrapper function
return ctx.scope.fieldName
}
// return null to not wrap this non-root resolver
return null
}
const wrapper = (fieldName) =>
async (resolve, source, args, context) => {
// Unless we're doing createUser, do an auth check
// 👍
if (fieldName === "createUser") return resolve()
const isAuthenticated = await getIsAuthenticated(context)
// 👍
if (isAuthenticated) return resolve()
throw new Error("Unauthorized");
}
const Permissions = makeWrapResolversPlugin(filter, wrapper)

Related

i want to redirect user after login based on their role and stop them to access this page in React.js

I had did this using Redux library and MongoDB and its works fine with this but now i am doing same thing with mysql so its not working well. This logic always redirect all users to admin dashboard. i want do like, if i do isAdmin="true" it will redirect to admin dashboard and stop to going coordinator dashboard. and if isCoordinator="true" then redirect to coordinator dashboard and not able to access admin dashboard. how can i that?
*This is my Admin.js file. where i did logic to private access path.
import { useSelector } from "react-redux";
import { Route, Routes, Link, useNavigate } from "react-router-dom";
import UsersList from "./UsersList";
import UsersEntry from "./UsersEntry";
import Projects from "./projects/Projects";
import "./Admin.css"
export default function Admin() {
let navigate = useNavigate();
const userState = useSelector(state=> state.loginUserReducer)
const {currentUser} =userState;
useEffect(() => {
// This code check Role of user who logged in and if not coordinator then restrict(stop) to going on this private page. By getItem and check === !currentUser.isCoordinator.
if(localStorage.getItem('currentUser') === null || !currentUser.isAdmin){
navigate('/coordinators');
if(localStorage.getItem('currentUser') === null || !currentUser.isCoordinator){
navigate('/');
}
}
}, [])
*This is UserAction.js fie.
export const loginUser = (user) => async (dispatch) => {
dispatch({type: "USER_LOGIN_REQUEST"});
try {
const response = await axios.post("http://localhost:4000/login",user);
console.log(response.data);
dispatch({type:"USER_LOGIN_SUCCESS", payload: response.data});
localStorage.setItem('currentUser',JSON.stringify(response.data));
window.location.href = "/admin";
} catch (error) {
dispatch({type: "USER_LOGIN_FAIL", payload: error})
}
}
*This is UserReducer.js.
switch (action.type){
case "USER_LOGIN_REQUEST":
return{
loading:true,
};
case "USER_LOGIN_SUCCESS":
return{
success: true,
loading: false,
currentusers: action.payload,
};
case "USER_LOGIN_FAIL":
return{
error: action.payload,
loading:false,
};
default:
return state;
}
};
*This is Store.js.
import thunk from 'redux-thunk';
import {composeWithDevTools} from 'redux-devtools-extension';
import {loginUserReducer} from './UserReducer';
const currentUser = localStorage.getItem('currentUser') ? JSON.parse(localStorage.getItem('currentUser')) : null
const rootReducer = combineReducers({loginUserReducer : loginUserReducer});
const initialState = {
loginUserReducer: {
currentUser : currentUser
}
}
const middleware = [thunk]
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
In your useEffect inside the dependency array pass the currentUser so whenever it is changed useEffect will be triggered. In current scenario when the array is empty it just happens on the page load the very first time.
Here is the possible thing to try:
useEffect(() => {
// This code check Role of user who logged in and if not coordinator then restrict(stop) to going on this private page. By getItem and check === !currentUser.isCoordinator.
if(localStorage.getItem('currentUser') === null || !currentUser.isAdmin){
navigate('/coordinators');
if(localStorage.getItem('currentUser') === null || !currentUser.isCoordinator){
navigate('/');
}
}
}, [currentUser]) <================
Secondly also check your nested if condition. You can use an else if as well so incase its not admin it will go inside if and if it is admin then it can go inside the else if block. But that is totally up to you, just a suggestion.

TypeScript correctly type a queryresult MySQL

I am using the mysql2/promise npm package for connecting and doing queries to a MySQL database. I got confused trying to set the correct typing for my query result, because I don't know in advance what type the result will be.
I have created a database class that has an async query method.
// Database.ts
import mysql, { Pool } from "mysql2/promise"; // import mysql2/promise
export class Database implements IDatabase {
logger: ILogger;
pool: Pool;
constructor(logger: ILogger) {
this.logger = logger;
// pool connection is set up here
}
async query(sql: string, options?: unknown): Promise<unknown> { // type?
const [rows] = await this.pool.query(sql, options);
return rows;
}
}
In my server code, I would like to be able do something like this:
// server.ts
import { Database } from "./core/Database";
const db = new Database(logger);
app.get("/", async (req, res) => {
const sql = `
select *
from users;
`;
const users: IUser[] = await db.query(sql); // get people
// do some logic x
// send response
res.json({
result: x
});
});
Using unknown doesn't work because I can't assign it to my type, using any does, but feels wrong. Is there a clean way to do this?
Type the function as:
async query<T = unknown>(sql: string, options?: unknown): Promise<T[]> {
Then use the function this way:
const users = await db.query<IUser>(sql);
With help of #Evert and this answer, I found a solution
I created following types:
export type DbDefaults = RowDataPacket[] | RowDataPacket[][] | OkPacket[] | OkPacket;
export type DbQueryResult<T> = T & DbDefaults;
Rewrote my method like this:
async query<T>(sql: string, options?: unknown): Promise<DbQueryResult<T[]>> {
const [result] = await this.pool.query<DbQueryResult<T[]>>(sql, options);
return result;
}
I can use it like this now:
const sql = `
select *
from users;
`;
const people = await db.query<IUser>(sql);
Just casting as T is also possible.
public async query<T>(sql: QueryString, parameters?: unknown[]): Promise<T[]> {
const [rows] = await this.pool.query(sql, parameters);
return rows as T[];
}

fetching data with react hook returns undefined on nested obj properties

Im trying to display data that has been fetched. but i cannot seem to display nested objects properties in react. Any ideas? if i log the data i first get a undefined, then the correct data.
my guess is that i need to wait for the data to be loaded then display it. but it does work for the title that is not in a nested obj.
function SingleBeneficiary({ match }) {
const [data, setData] = useState({ data: []});
const id = match.params.id
useEffect(() => {
async function fetchData() {
const response = await fetch(`http://localhost:8081/v1/beneficiary/${id}`);
const jsonData = await response.json()
setData(jsonData)
}
fetchData();
}, [])
return (
{data.title} // works
{data.address.careOf} // dont work
The data
{
"title":"myTitle",
"address":{
"careOf": "my adress"
}
}
Can you try like this?
I set initial data to null, and in return I check if it is not null.
If address can be null, additional null check is required.
function SingleBeneficiary({ match }) {
const [data, setData] = useState(null);
const id = match.params.id
useEffect(() => {
async function fetchData() {
const response = await fetch(`http://localhost:8081/v1/beneficiary/${id}`);
const jsonData = await response.json()
setData(jsonData)
}
fetchData();
}, [])
return (
<div>
{data && (
<div>
<p>data.title</p>
<p>data.address.careOf</p>
</div>
)}
</div>
);
}
You should check if address has careOf property before using it because first time data will be undefined and in second render it will have the data after the api call.
{data.address && data.address.careOf}
For anyone who is having a similar issue(i.e. fetching data via api and only the first time it runs, it will show the data as undefined but after manual refreshing, it works fine), here is a quick and sketchy addition you might consider alongside with 1. "Inline If with Logical && Operator" method and 2. using useState for checking if the api loading is over. With those three, mine worked.
Try fetching the desired data in the previous page of your app; in this case, add the following lines in any page you'll see before "SingleBeneficiary".
const response = await fetch(`http://localhost:8081/v1/beneficiary/${id}`);
const jsonData = await response.json()
Maybe it has to do with npm cache, but not really sure what's going on.
replace
return (
{data.title}
{data.address.careOf}
)
with
return (
{data?.title}
{data?.address?.careOf}
)

Is it possible to perform an action with `context` on the init of the app?

I'm simply looking for something like this
app.on('init', async context => {
...
})
Basically I just need to make to calls to the github API, but I'm not sure there is a way to do it without using the API client inside the Context object.
I ended up using probot-scheduler
const createScheduler = require('probot-scheduler')
module.exports = app => {
createScheduler(app, {
delay: false
})
robot.on('schedule.repository', context => {
// this is called on startup and can access context
})
}
I tried probot-scheduler but it didn't exist - perhaps removed in an update?
In any case, I managed to do it after lots of digging by using the actual app object - it's .auth() method returns a promise containing the GitHubAPI interface:
https://probot.github.io/api/latest/classes/application.html#auth
module.exports = app => {
router.get('/hello-world', async (req, res) => {
const github = await app.auth();
const result = await github.repos.listForOrg({'org':'org name});
console.log(result);
})
}
.auth() takes the ID of the installation if you wish to access private data. If called empty, the client will can only retrieve public data.
You can get the installation ID by calling .auth() without paramaters, and then listInstallations():
const github = await app.auth();
const result = github.apps.listInstallations();
console.log(result);
You get an array including IDs that you can in .auth().

How to convert the value stored in Observable<any> to a string, in typescript?

Hi I am new to Angular and TypeScript. I need the value of an Observable in the format of a string, how does one do this?
the BmxComponent file
export class BmxComponent {
asyncString = this.httpService.getDataBmx();
currentStock = this.httpService.getDataBmx2(); //this is what I want to covert to a string so I can pass it to onSubmit()
onSubmit() {
const asnNumm = this.currentStock; // passing it here changes my database, see below
this.httpService.sendData({ stock: asnNumm })
.subscribe(
data => console.log(data),
error => console.log(error)
);
}
}
the HttpService file
export class HttpService {
constructor(private http: Http) {}
getDataBmx() {
return this.http.get('https://the-bicycle-shop.firebaseio.com/products/Bicycles/bmx/stock.json')
.map((response: Response) => response.json());
}
getDataBmx2() {
return (this.http.get('https://the-bicycle-shop.firebaseio.com/products/Bicycles/bmx/stock.json'));
}
sendData(newStock: any) {
const body = JSON.stringify(newStock);
const headers = new Headers();
headers.append('Content-Type', 'application/json');
return this.http.patch('https://the-bicycle-shop.firebaseio.com/products/Bicycles/bmx.json', body, {
headers: headers
})
.map((data: Response) => data.json())
.catch(this.handleError);
}
private handleError(error: any) {
console.log(error);
return Observable.throw(error.json());
}
}
the html file
<p>{{asyncString | async}}</p> // displays 1234 which is the correct value
<p>{{asyncString}}</p> // displays [object Object]
<p>{{currentStock}}</p> // displays [object Object]
<button class="btn btn-success" (click)="onSubmit()">Change Database</button>
my database before onSubmit() (used when I click the Change Database button)
Bicycles
|
---bmx
|
---stock = 1234;
my database after onSubmit()
Bicycles
|
--- bmx
|
---stock
|
--- _isScalar = false
I am using Firebase for this.
I know it will work with a string because I tested it with like this:
onSubmit() {
const asnNumm = "33333" //the change to test it
this.httpService.sendData({ stock: asnNumm })
.subscribe(
data => console.log(data),
error => console.log(error)
);
}
Which does this to my database
Bicycles
|
---bmx
|
---stock = 33333
I understand that currentStock would hold the same value that is currently stored in my database, so it would make no difference, but I want to change it once I have converted it to a string.
Basically I want to change "stock" in my database, but by a fixed amount each time I press the Change Database button, for example, minus 1 it each time it is pressed.
Subscribe to the observable to get the result and call onSubmit when you receive the value:
currentStock = this.httpService.getDataBmx2()
.subscribe(val => this.onSubmit(val));
Objects has toString method that you can implement to show the value of object, or convert it to string with JSON.stringify() like this
this.httpService.sendData({ stock: asnNumm })
.subscribe(
data => console.log(JSON.stringify(data)),
error => console.log(error)
);
You have to map to the response object to get data, to get data as text you can query response object
getDataBmx2() {
return this.http.get('https://the-bicycle-shop.firebaseio.com/products/Bicycles/bmx/stock.json')
.map((response: Response) => response.text());
}
export class BmxComponent {
currentStock: string;
this.httpService.getDataBmx2().subscribe(s => this.currentStock = s); //this is what I want to covert to a string so I can pass it to onSubmit()
onSubmit() {
const asnNumm = this.currentStock; // passing it here changes my database, see below