Generate Typesafe SDK for Hasura in Backend (#68)

This commit is contained in:
Hammad Jutt
2020-08-15 02:26:29 -06:00
committed by GitHub
parent 357f292ace
commit 7e0e42a2ff
13 changed files with 199 additions and 107 deletions

View File

@@ -5,6 +5,12 @@
"rules": {
"@typescript-eslint/naming-convention": "off"
}
},
{
"files": ["./src/handlers/**/*.ts"],
"rules": {
"@typescript-eslint/explicit-module-boundary-types": "off"
}
}
]
}

View File

@@ -11,3 +11,16 @@ generates:
noSchemaStitching: true
avoidOptionals: true
maybeValue: 'T extends PromiseLike<infer U> ? Promise<U | null> : T | null | undefined'
./src/lib/autogen/sdk.ts:
schema: '../codegen/schema.graphql'
documents:
- ./src/handlers/**/(!(*.d)).ts
- ./src/handlers/**/*.graphql
plugins:
- typescript
- typescript-operations
- typescript-graphql-request
- add:
content: '/* eslint-disable */'
config:
immutableTypes: true

View File

@@ -11,7 +11,8 @@
"dev-ts": "ts-node-dev --exit-child --respawn --transpile-only -- src/index.ts",
"typecheck": "yarn build",
"precommit": "yarn lint-staged",
"generate": "graphql-codegen --config=codegen.yml"
"generate": "graphql-codegen --config=codegen.yml",
"lintfix": "eslint --fix"
},
"author": "",
"license": "ISC",
@@ -23,6 +24,7 @@
"express": "^4.17.1",
"express-graphql": "^0.11.0",
"graphql": "^15.3.0",
"graphql-tag": "^2.10.4",
"graphql-tools": "^6.0.15",
"node-fetch": "^2.6.0",
"graphql-request": "^3.0.0-next.4"

View File

@@ -1,37 +1,7 @@
import Box, { VerifiedAccounts } from '3box';
import { Request, Response } from 'express';
import { gql } from 'graphql-request';
import { hasuraQuery } from '../../../lib/hasuraHelpers';
type PlayerResult = {
Player: Array<{
id: string;
ethereum_address: string | null | undefined;
}>;
};
const getPlayerQuery = gql`
query GetPlayer($playerId: uuid!) {
Player(where: { id: { _eq: $playerId } }) {
id
ethereum_address
}
}
`;
const upsertAccount = gql`
mutation upsert_Account($objects: [Account_insert_input!]!) {
insert_Account(
objects: $objects
on_conflict: {
constraint: Account_identifier_type_player_key
update_columns: [identifier]
}
) {
affected_rows
}
}
`;
import { client } from '../../../lib/hasuraClient';
export const updateBoxProfileHandler = async (
req: Request,
@@ -45,11 +15,9 @@ export const updateBoxProfileHandler = async (
throw new Error('expected role player');
}
const data = await hasuraQuery<PlayerResult>(getPlayerQuery, {
playerId,
});
const data = await client.GetPlayer({ playerId });
const ethAddress = data.Player[0]?.ethereum_address;
const ethAddress = data.Player_by_pk?.ethereum_address;
if (!ethAddress) {
throw new Error('unknown-player');
@@ -69,7 +37,7 @@ async function updateVerifiedProfiles(
const updatedProfiles: string[] = [];
if (verifiedProfiles.github) {
const result = await hasuraQuery(upsertAccount, {
const result = await client.UpsertAccount({
objects: [
{
player_id: playerId,
@@ -78,14 +46,14 @@ async function updateVerifiedProfiles(
},
],
});
if (result.affected_rows === 0) {
if (result.insert_Account?.affected_rows === 0) {
throw new Error('Error while upserting github profile');
}
updatedProfiles.push('github');
}
if (verifiedProfiles.twitter) {
const result = await hasuraQuery(upsertAccount, {
const result = await client.UpsertAccount({
objects: [
{
player_id: playerId,
@@ -94,7 +62,7 @@ async function updateVerifiedProfiles(
},
],
});
if (result.affected_rows === 0) {
if (result.insert_Account?.affected_rows === 0) {
throw new Error('Error while upserting github profile');
}
updatedProfiles.push('twitter');

View File

@@ -1,7 +1,7 @@
import { did } from '@metafam/utils';
import { Request, Response } from 'express';
import { getPlayer } from './users';
import { getOrCreatePlayer } from './users';
const unauthorizedVariables = {
'X-Hasura-Role': 'public',
@@ -30,7 +30,7 @@ export const authHandler = async (
return;
}
const player = await getPlayer(claim.iss);
const player = await getOrCreatePlayer(claim.iss);
const hasuraVariables = {
'X-Hasura-Role': 'player',

View File

@@ -1,52 +1,18 @@
import { hasuraQuery } from '../../lib/hasuraHelpers';
import { client } from '../../lib/hasuraClient';
const getPlayerQuery = `
query GetPlayerFromETH ($ethereum_address: String) {
Player(
where: {
ethereum_address: { _eq: $ethereum_address }
}
) {
id
}
}
`;
const createProfileMutation = `
mutation CreateAccountFromETH ($ethereum_address: String!, $username: String!) {
insert_Player(
objects: {
username: $username,
ethereum_address: $ethereum_address
}) {
affected_rows
returning {
id
username
ethereum_address
}
}
}
`;
interface IPlayer {
id: string;
username: string;
ethereum_address: string;
}
export async function createPlayer(ethAddress: string): Promise<IPlayer> {
const resProfile = await hasuraQuery(createProfileMutation, {
async function createPlayer(ethAddress: string) {
const resProfile = await client.CreatePlayerFromETH({
ethereum_address: ethAddress,
username: ethAddress,
});
if (resProfile.insert_Player.affected_rows !== 1) {
if (resProfile.insert_Player?.affected_rows !== 1) {
throw new Error('Error while creating player');
}
return resProfile.insert_Player.returning[0];
}
export async function getPlayer(ethAddress: string): Promise<IPlayer> {
const res = await hasuraQuery(getPlayerQuery, {
export async function getOrCreatePlayer(ethAddress: string) {
const res = await client.GetPlayerFromETH({
ethereum_address: ethAddress,
});

View File

@@ -0,0 +1,30 @@
import { gql } from 'graphql-request/dist';
export const CreatePlayerFromETH = gql`
mutation CreatePlayerFromETH($ethereum_address: String!, $username: String!) {
insert_Player(
objects: { username: $username, ethereum_address: $ethereum_address }
) {
affected_rows
returning {
id
username
ethereum_address
}
}
}
`;
export const UpsertAccount = gql`
mutation UpsertAccount($objects: [Account_insert_input!]!) {
insert_Account(
objects: $objects
on_conflict: {
constraint: Account_identifier_type_player_key
update_columns: [identifier]
}
) {
affected_rows
}
}
`;

View File

@@ -0,0 +1,18 @@
import { gql } from 'graphql-request/dist';
export const GetPlayer = gql`
query GetPlayer($playerId: uuid!) {
Player_by_pk(id: $playerId) {
id
ethereum_address
}
}
`;
export const GetPlayerFromEth = gql`
query GetPlayerFromETH($ethereum_address: String) {
Player(where: { ethereum_address: { _eq: $ethereum_address } }) {
id
}
}
`;

View File

@@ -0,0 +1,13 @@
import { GraphQLClient } from 'graphql-request';
import { CONFIG } from '../config';
import { getSdk } from './autogen/sdk';
export const client = getSdk(
new GraphQLClient(CONFIG.graphqlURL, {
headers: {
'Content-Type': 'application/json',
'x-hasura-access-key': CONFIG.adminKey,
},
}),
);

View File

@@ -1,22 +0,0 @@
import { GraphQLClient } from 'graphql-request';
import { Variables } from 'graphql-request/dist/types';
import { CONFIG } from '../config';
export async function hasuraQuery<T = any>(
query: string,
variables?: Variables,
): Promise<T> {
const graphQLClient = new GraphQLClient(CONFIG.graphqlURL, {
headers: {
'Content-Type': 'application/json',
'x-hasura-access-key': CONFIG.adminKey,
},
});
try {
return await graphQLClient.request<T>(query, variables);
} catch (error) {
throw new Error(JSON.stringify(error, undefined, 2));
}
}