While trying to inject prepared statements with MSSQL (tedious) - once I'm trying to execute the request I'm getting the following error:
Database connection failed: Requests can only be made in the LoggedIn state, not the SentClientRequest state
The imports:
import { Connection, Request } from 'tedious';
The hook:
public static async afterConnect(connection, options): Promise<void> {
const client = connection['resource'] as Connection;
let request = new Request('select 42',(err, rowCount, rows) => {
console.log(`${err} ${rowCount} rows`);
});
client.execSql(request);
}
According to tedious I need to chain the request after the previous one, sequelize doesn't seem to pass that data, is there a way to work around this issue?
I've also created an issue on Sequelize GitHub
took some digging inside sequelize code, but found a work around - if anyone has a similar issue.
public static async afterConnect(connection, options): Promise<void> {
const mssql = connection as { resource: Connection; previous: Promise<unknown> };
await new Promise((resolve) => {
let request = new Request('select 42', (err, rowCount) => {
logger.debug(method, `${err} - ${rowCount} rows`);
resolve();
});
await mssql.previous; // failsafe?
mssql.resource.execSql(request);
});
}
Related
I am working on a react app with redux. I did implement thunk actions to:
Create a web3modal instance
Register a ethersjs provider
Register a ethersjs signer
Everything is very basic and simple.
However whenever I try to make use of the on events (doesn't matter if provider oder contract), they simply won't fire.
I have 2 files:
walletSlice.ts which will handle all the redux action and reducer logic with #reduxjs/toolkit .
wallet-api.ts which has all the relevant functions to interact with the wallet.
The walletSlice.ts relevant part looks exactly like this:
export const connectWallet = createAsyncThunk(
'web3wallet/connectWallet',
async (arg, thunkApi) => {
const instance = await WalletAPI.registerWalletInstance();
provider = await WalletAPI.registerWalletProvider(instance);
signer = await WalletAPI.registerWalletSigner(provider);
return Promise.resolve();
}
);
The wallet-api.ts relevant parts look exactly like this:
import { ethers, providers } from 'ethers';
import Web3Modal from 'web3modal';
// get Web3Modal Instance
export async function registerWalletInstance(): Promise<Web3Modal> {
const providerOptions = {};
const web3Modal = new Web3Modal({
providerOptions,
});
const instance = await web3Modal.connect();
return Promise.resolve(instance);
}
/**
* register Wallet provider.
* Events on provider #see https://docs.ethers.io/v5/api/providers/provider/#Provider--event-methods
* Implementing the EIP-1193 Standard #see https://eips.ethereum.org/EIPS/eip-1193
*/
export async function registerWalletProvider(
instance: any
): Promise<providers.JsonRpcProvider> {
const provider = new ethers.providers.Web3Provider(instance);
// Subscribe to accounts change
provider.on('accountsChanged', (accounts: string[]) => {
console.log(accounts);
});
// Subscribe to chainId change
provider.on('chainChanged', (chainId: number) => {
console.log(chainId);
});
// Subscribe to provider connection
provider.on('connect', (info: { chainId: number }) => {
console.log(info);
});
// Subscribe to provider disconnection
provider.on('disconnect', (error: { code: number; message: string }) => {
console.log(error);
});
provider.on('error', (tx) => {
// Emitted when any error occurs
console.log({ tx });
});
return Promise.resolve(provider);
}
// register Wallet signer.
export async function registerWalletSigner(
provider: providers.JsonRpcProvider
): Promise<providers.JsonRpcSigner> {
const signer = provider.getSigner();
return Promise.resolve(signer);
}
None of the provider.on() events will fire. I've tried to change networks from rinkeby to polygon or mainnet, but nothing happens. When I disconnect from the site, nothing happens. It is the same with all provider events as shown in wallet-api.ts. I did try the same approach with another file called contract-api.ts. However the contract events won't fire either.
I tried to use the provider.on() events with useEffect() or useCallback(). Moved the code to a standalone tsx. But nothing happened.
await web3Modal.connect() already returns a provider;
try to use that one instead of new ethers.providers.Web3Provider(instance);
I recently started using react-query and have encountered the issue that always stale data is returned and no call to server is made. here is the react query related code:
export function useGetAccount(id: number){
return useQuery([`account${id}`, id], async (args) => {
const [key, accountId] = args.queryKey
const [acc, teams, modules] = await Promise.all([
getAccount(),
getTeams(),
getModules()])
let account: AccountDetail = {
accountId: acc.accountId,
userId: acc.userId,
companyId: acc.companyId,
login: acc.login,
email: acc.email,
description: acc.description,
isActive: acc.isActive,
providers: acc.providers,
teams: teams,
modules: modules
}
return account
async function getAccount() {
const api = createApi() // <= axios wrapper
const { data } = await api.get(`accounts/${accountId}`, undefined, undefined)
return data as AccountModel
}
async function getTeams() {
const api = createApi()
const { data } = await api.get(`accounts/${accountId}/teams`, undefined, undefined)
const { collection } = data as ResponseCollectionType<AccountTeam>
return collection
}
async function getModules() {
const api = createApi()
const { data } = await api.get(`accounts/${accountId}/resources`, undefined, undefined)
const { collection } = data as ResponseCollectionType<ModuleAccessModel>
return collection
}
})
}
I even reduced the cache time but still to no avail. I do not see any calls made to server side except after a long delay or if I open the browser in incognito mode then first time the data is fetched and then no call is made.
this is used in a component which shows the details and is passed the id as a prop. everything is working fine except that the data is the one which was retrieved first time and even a refresh (F5) returns the stale data.
what changes do I need to make in this case?
[observation]: Ok, it does make a call but only after exact 5 minutes.
well the problem is not in react-query but in axios, described here Using JavaScript Axios/Fetch. Can you disable browser cache?
I used the same solution i.e. appending timestamp to the requests made by axios and everything worked fine.
In Next.js we have a feature of dynamic routing configuration on getStaticPaths method. I need to fetch the data from database based on that data, I need to set dynamic routes. But when ever I try to make a database call, I am getting an error mentioned below.
export async function getStaticPaths() {
const client = await MongoClient.connect('connection string');
const dataBase = client.db();
const collections = dataBase.collection('table name');
let result = await collections .find({}, { id: 1 }).toArray();
client.close();
let paths = result.map((data) => ({
params: { id: data.id.toString() }
}));
console.log(paths);
return {
fallback: false,
paths: paths
};
}
error that I can see,
./node_modules/mongodb/lib/url_parser.js:7:0
Module not found: Can't resolve 'dns'
Please help. Thanks in advance.
I am building a React application with GraphQL using AWS AppSync with DynamoDB. My use case is that I have a table of data that is being pulled from a DynamoDB table and displayed to the user using GraphQL. I have a few fields that are being updated by step functions running on AWS. I need those fields to be automatically updated for the user much like a subscription from GraphQL would do but I found out that subscriptions are tied to mutations and thus an update to the database from step functions will not trigger a subscription update on the frontend. To get around this I am using the following:
useEffect(() => {
setTimeout(getSubmissions, 5 * 1000)
})
Obviously this is a lot of overfetching and will probably incur unnecessary expense. I have looked for a better solution and come across DynamoDB streams but DynamoDB streams can't help me if they can't trigger the frontend to refresh the component. There has to be a better solution than what I have come up with.
Thanks!
You are correct, in AWS AppSync, to trigger a subscription publish you must trigger a GraphQL mutation.
but I found out that subscriptions are tied to mutations and thus an
update to the database from step functions will not trigger a
subscription update on the frontend.
If you update your DynamoDB table directly via step functions or via DynamoDB streams, then AppSync has no way to know the data refreshed.
Why don't you have your step function use an AppSync mutation instead of updating your table directly? That way you can link a subscription to the mutation and have your interested clients get pushed updates when the data is refreshed.
Assuming you are using Cognito as your authentication for your AppSync application, you could set a lambda trigger on the dynamo table that generates a cognito token, and uses that make an authorized request to your mutation endpoint. NOTE: in your cognito userpool>app clients page, you will need to check the Enable username password auth for admin APIs for authentication (ALLOW_ADMIN_USER_PASSWORD_AUTH) box to generate a client secret.
const AWS = require('aws-sdk');
const crypto = require('crypto');
var jwt = require('jsonwebtoken');
const secrets = require('./secrets.js');
var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
var config;
const adminAuth = () => new Promise((res, rej) => {
const digest = crypto.createHmac('SHA256', config.SecretHash)
.update(config.userName + config.ClientId)
.digest('base64');
var params = {
AuthFlow: "ADMIN_NO_SRP_AUTH",
ClientId: config.ClientId, /* required */
UserPoolId: config.UserPoolId, /* required */
AuthParameters: {
'USERNAME': config.userName,
'PASSWORD': config.password,
"SECRET_HASH":digest
},
};
cognitoidentityserviceprovider.adminInitiateAuth(params, function(err, data) {
if (err) {
console.log(err.stack);
rej(err);
}
else {
data.AuthenticationResult ? res(data.AuthenticationResult) : rej("Challenge requested, to verify, login to app using admin credentials");
}
});
});
const decode = auth => new Promise( res => {
const decoded = jwt.decode(auth.AccessToken);
auth.decoded = decoded
res(auth);
});
//example gql query
const testGql = auth => {
const url = config.gqlEndpoint;
const payload = {
query: `
query ListMembers {
listMembers {
items{
firstName
lastName
}
}
}
`
};
console.log(payload);
const options = {
headers: {
"Authorization": auth.AccessToken
},
};
console.log(options);
return axios.post(url, payload, options).then(data => data.data)
.catch(e => console.log(e.response.data));
};
exports.handler = async (event, context, callback) => {
await secrets() //some promise that returns your keys object (i use secrets manager)
.then( keys => {
#keys={ClientId:YOUR_COGNITO_CLIENT,
# UserPoolId:YOUR_USERPOOL_ID,
# SecretHash:(obtained from cognito>userpool>app clients>app client secret),
# gqlEndpoint:YOUR_GRAPHQL_ENDPOINT,
# userName:YOUR_COGNITO_USER,
# password:YOUR_COGNITO_USER_PASSWORD,
# }
config = keys
return adminAuth()
})
.then(auth => {
return decode(auth)
})
.then(auth => {
return testGql(auth)
})
.then( data => {
console.log(data)
callback(null, data)
})
.catch( e => {
callback(e)
})
};
I've followed Apollo's docs for setting up GraphQL subscriptions on both the client and server, and though I'm 90% there, I can't figure out how to set up subscription channels and how to connect mutations to those channels so that whenever a mutation occurs, the server pushes the new data to the client. (For content, I'm making a Reddit clone where people post topics and others comment on it. So when you see "Topics" or "TopicList," think of those as posts.)
So far, I have set up Apollo Client for subscriptions successfully:
const wsClient = new SubscriptionClient('ws://localhost:3001/subscriptions', {
reconnect: true
});
const networkInterface = createNetworkInterface({
uri: '/graphql',
opts: {
credentials: 'same-origin'
}
});
const networkInterfaceWithSubscriptions = addGraphQLSubscriptions(
networkInterface,
wsClient,
);
const client = new ApolloClient({
networkInterface: networkInterfaceWithSubscriptions,
dataIdFromObject: o => o.id
});
And I've set up my back-end for subscriptions as well. Here's my server.js file:
//===========================================================
//Subscription Managaer
//===========================================================
const pubsub = new PubSub();
const subscriptionManager = new SubscriptionManager({
schema: schema,
pubsub: pubsub
});
//=====================================
//WebSocket + Express Server
//=====================================
const server = createServer(app);
//setup listening port
server.listen(3001, ()=>{
new SubscriptionServer(
{
subscriptionManager: subscriptionManager,
onConnect: (connectionParams, webSocket) => {
console.log('Websocket connection established');
},
onSubscribe: (message, params, webSocket) => {
console.log("The client has been subscribed", message, params);
},
onUnsubsribe: (webSocket) => {
console.log("Now unsubscribed");
},
onDisconnect: (webSocket) => {
console.log('Now disconnected');
}
},
{
server: server,
path: '/subscriptions',
});
console.log('Server is hot my man!');
})
I know these are successful, because I get the "Websocket connection established" message logged in my terminal.
Next is the actual subscription - I've created a subscription schema type (just like queries and mutations):
const SubscriptionType = new GraphQLObjectType({
name: 'Subscription',
fields: () => ({
topicAdded: {
type: TopicType,
args: {repoFullName: {type: GraphQLString}}, //I don't understand what repoFullName is - I was trying to follow the Apollo docs, but they never specified that
resolve(parentValue, args){
return parentValue;
}
}
})
});
module.exports = SubscriptionType;
and incorporated it into my root schema. So when I check out GraphiQL, I see: this subscription available in the Docs side menu
My GraphiQIL UI showing the subscriptionSchema successfully
In my React component, I successfully 'subscribe' to it using Apollo's subscribeToMore method:
const TOPICS_SUBSCRIPTION = gql`
subscription OnTopicAdded($repoFullName: String){
topicAdded(repoFullName: $repoFullName){
id
}
}
`;
class TopicList extends Component {
componentDidMount() {
this.createMessageSubscription = this.props.data.subscribeToMore({
document: TOPICS_SUBSCRIPTION,
// updateQuery: (previousState, {subscriptionData}) => {
// const newTopic = subscriptionData.data.Topic.node
// const topics = previousState.findTopics.concat([newTopic])
// return {
// findTopics: topics
// }
// },
onError: (err) => console.error(err)
})
} //...
And I get my "The client has been subscribed" message logged into my terminal. But this is where I'm stuck. I've read about the SetupFunction for the SubscriptionManager, but that isn't included in Apollo's docs. And I can't find how to map a 'createTopic' mutation to this subscription so that whenever someone adds a new topic, it pops up in TopicList.
I realize this is really long, but I've been pulling my hair out tyring to figure out what's the next step. Any help would be much appreciated!! Thank you for reading!
Yes you are missing the setup function. You could take a look at this links GraphQL subscription docu or this example.
How it should work:
First you need the channel on which you publish the changed data. In your case it could look like this:
const manager = new sub.SubscriptionManager({
schema,
pubSub,
setupFunctions: {
topicAdded: (options, args) => ({ // name of your graphQL subscription
topicAddedChannel: { // name of your pubsub publish-tag
filter: (topic) => {
console.log(topic); //should always show you the new topic if there is a subscribed client
return true; // You might want to add a filter. Maybe for guest users or so
},
},
}),
},
});
And here you see the need of the args: {repoFullName: {type: GraphQLString}} argument in the subscription. If you want to filter the subscription dependent on the "repoName". Meaning that only the client with a subscription with the "repoName" as argument gets an update.
Next you need a place where you call the pubsub.publish function. In your case after the add topic mutation has passed. Could look like this:
...
const topic = new Topic(/* args */);
topic.save((error, topic) => {
if (!error) {
pubsub.publish("topicAddedChannel", topic);
}
...
});
....