Is there any way to send a message automatically in converse.js - xmpp

Is there any way to send a message automatically, I found a sendMessage trigger but it doesn't work, maybe I am doing wrong something
export const msgUser = (viewerChannelCredentials: any) => {
const {username, channel} = viewerChannelCredentials;
window.converse.plugins.add('msg-user', {
dependencies: [],
initialize: async function () {
const _converse = this._converse;
return _converse.api.trigger('sendMessage', {
chatbox: _converse.ChatBox | _converse.ChatRoom,
message: 'abc',
});
},
});
};
Another solution found that also doesn't work, it throws an error which Cannot read properties of undefined (reading '$msg')
export const msgUser = (viewerChannelCredentials: any) => {
const {username, channel} = viewerChannelCredentials;
window.converse.plugins.add('msg-user', {
dependencies: [],
initialize: async function () {
const _converse = this._converse;
var msg = _converse.env.$msg({
from: 'juliet#example.com/balcony',
to: 'romeo#example.net',
type: 'chat',
});
_converse.send(msg);
},
});
};

Calling api.trigger('sendMessage' only fires an event with the name "sendMessage", it doesn't actually send a message.
To send a message, you can call the sendMessage method on a ChatBox.
For example:
const chat = await api.chats.get(jid)
chat.sendMessage({ body: 'Hello world' });

Related

Jest mock Typeorm Datasource in unit tests (without nestjs)

I am working on creating unit tests for a project that uses Typeorm without Nestjs. The file I am creating unit tests for uses queryRunner to start a transaction. My problem is, I am not able to mock the Datasource. I tried multiple ways but the mock is never getting called to replace the actual Datasource that has access to the postgresql database. I saw some solutions to mock it, but they all use Nestjs, which I don't use in my case.
The error I am having in the tests right now is:
Received promise rejected instead of resolved
Rejected to value: [TypeORMError: Driver not Connected]
Any help would be highly appreciated since I'm not an expert in unit tests.
Here's an example of the code that I am having the issue with (replaced some names tho):
datasource.ts
//database config is defined in this file
export const datasource: DataSource = new DataSource(some_config);
dummy.service.ts
export const dummyService = () => {
//datasource is imported from the above file
const queryRunner = datasource.createQueryRunner();
await queryRunner.startTransaction();
try {
const foundObject = await queryRunner.manager.getRepository(MyObject).findOne({
where: { id: someId },
lock: { mode: 'pessimistic_write' },
});
//some more database calls
} catch (error) {
await queryRunner.rollbackTransaction();
} finally {
await queryRunner.release();
}
}
mock.datasource.ts
import { DataSource } from 'typeorm';
export const dataSourceMockFactory: () => MockType<DataSource> = jest.fn(
() => ({
createQueryRunner: jest.fn().mockImplementation(() => ({
connect: jest.fn(),
startTransaction: jest.fn(),
release: jest.fn(),
rollbackTransaction: jest.fn(),
manager: {
getRepository: jest.fn().mockImplementation(() => ({
create: jest.fn(),
findOne: jest.fn(() => {
return getMyDummyObject();
}),
})),
save: jest.fn(),
},
})),
}),
);
export type MockType<T> = {
// eslint-disable-next-line #typescript-eslint/ban-types
[P in keyof T]?: jest.Mock<{}>;
};
dummy.unit.test.ts
describe('dummy service test', () => {
let dataSourceMock: MockType<Typeorm.DataSource>;
beforeEach(async () => {
// This method did not work
jest.mock('typeorm', () => {
const actual = jest.requireActual('typeorm');
return {
...actual,
DataSource: dataSourceMockFactory(),
};
});
// The below method did not work either
jest.mock('./../../db/datasource', () => ({
datasource: dataSourceMockFactory(),
}));
});
afterEach(() => {
jest.clearAllMocks();
});
it('should test dummy service with transaction', async () => {
// getting an error here
await expect(
dummyFunction(),
).resolves.not.toThrow();
});
});

React-query is not updating the state

I recently started to use react query, but I don't quite understand yet how the state works under the hood.
I have a query function that logs in the user:
async function signin(
model: AuthenticationControllerSignInRequest | null
): Promise<any> {
if (model) {
queryClient.invalidateQueries()
const response = await CalyxApi.authApi().authenticationControllerSignIn(
model
);
LocalStorage.set(LOCAL_STORAGE_KEY.AUTH, response.data.authToken);
return response.data.authToken
}
return loadFromStorage()
}
Inside I use loadFromStorage function that gets authToken from local storage.
async function loadFromStorage(): Promise<AuthTokenModel | undefined> {
const storedAuth = LocalStorage.get(LOCAL_STORAGE_KEY.AUTH);
if (storedAuth) {
if (new Date(storedAuth.validUntil) < new Date()) {
LocalStorage.remove(LOCAL_STORAGE_KEY.AUTH)
return undefined;
}
return storedAuth;
} else {
return undefined;
}
}
In my Login components I use the query hook passing in signin function and formik that refetches on submit:
...
const { data: auth, refetch, isLoading: authLoading } = useQuery(['auth', signinModel], () => authActions.signin(signinModel), { enabled: false });
const formik = useFormik({
validateOnChange: false,
validateOnBlur: false,
initialValues: {
email: '',
password: '',
},
validationSchema: loginFormSchema,
onSubmit: async (values) => {
await setSigninModel({
email: values.email,
password: values.password
})
await refetch()
}
});
...
This works just fine. I am able to authenticate the user which should prompt another function that fetches the user from DB:
const { data: auth } = useQuery(['auth'], () => authActions.signin(null))
const userId = auth?.userId;
console.log('useUserActions: ', userId)
async function fetchUser(): Promise<UserModel | undefined> {
if (!userId) {
errorSuccessActions.throwError('USER ID IS UNDEFINED');
return
}
const result = await CalyxApi.userApi().userControllerGetUser(userId)
if (result.data) {
const user = result.data.user
return user
}
errorSuccessActions.throwError('USER NOT FOUND IN DB');
return
}
function useFetchUser(reactQueryOptions?: {}) {
return useQuery<UserModel | undefined>(
["user", userId],
() => fetchUser(), {
...reactQueryOptions, refetchOnWindowFocus: false,
onError: (err) => errorSuccessActions.throwError(err),
onSuccess: (data) => {
queryClient.setQueryData(['user'], data);
},
initialData: () => {
const user: UserModel | undefined = queryClient.getQueryData('user')
if (user) {
return user
}
return undefined
}
})
}
This expects userId that I get from ´auth´ state. Problem is that I actually don't get it after signin function fires. I only get the state updated if I reload the page or i refocus on the tab.
I have a console.log that should log the userId but it always returns undefined. Only when I refocus on the window will it return the userId prompting to fetch the user.
I am not sure what am I missing to get the updated auth state and to get the userId right after I sign in.

Nexjs + SWR: API resolved without sending a response for /api/projects/<slug>, this may result in stalled requests

Since on first render I was not able to get the router.query I am passing the params from getServerSideProps as follows:
export async function getServerSideProps(context) {
return {
props: { params: context.params },
};
}
Then in the function am trying to do the API call but am getting the API stalled error
API resolved without sending a response for
/api/projects/nichole_robel23, this may result in stalled requests.
This is my code:
export default function Project({ params }) {
const { slug } = params;
let [projectData, setProjectData] = useState([]);
let [loading, setLoading] = useState(true);
const { data } = useSWR('http://localhost:3000/api/projects/' + slug);
useEffect(() => {
if (data) {
setProjectData(data.data.project);
setLoading(false);
}
}, [data]);
......
I have global SWRCofig as follows
<SWRConfig value={{ fetcher: (url) => axios(url).then(r => r.data) }}>
<Layout>
<Component {...pageProps} />
</Layout>
</SWRConfig>
Any way to solve the problem?
You are missing your fetcher–the function that accepts the key of SWR and returns the data, so the API is not being called.
You are also not returning a response correctly from the API–this is most likely a case of not waiting for a promise/async to be fulfilled correctly.
CLIENT
const fetcher = (...args) => fetch(...args).then((res) => res.json());
export default function Home({ params }) {
const { slug } = params;
const [projectData, setProjectData] = useState([]);
const [loading, setLoading] = useState(true);
const { data } = useSWR(`http://localhost:3000/api/projects/${slug}`, fetcher);
useEffect(() => {
if (data) {
setProjectData(data);
setLoading(false);
}
}, [data]);
API
const getData = () => {
return new Promise((resolve, reject) => {
// simulate delay
setTimeout(() => {
return resolve([{ name: 'luke' }, { name: 'darth' }]);
}, 2000);
});
}
export default async (req, res) => {
// below will result in: API resolved without sending a response for /api/projects/vader, this may result in stalled requests
// getData()
// .then((data) => {
// res.status(200).json(data);
// });
// better
const data = await getData();
res.status(200).json(data);
}

Best practice for using React hooks and Context API to update global state and fetch/provide data from multiple endpoints

I am new to React hooks/Context API. I have read the React hook/context docs, and I am still having trouble with the following:
My attempts to update global state by multiple consumer components
currently causes frequent overwriting of context state due to
rerendering (e.g., activity or details state is sometimes
null/undefined). This probably is why...
... I am getting 400 (bad request) and/or 500 (server) errors on random refreshes of the page (~30% of the time content loads as
expected, ~70% errors are thrown. I believe this is happening
because we have various context states that are being called
asynchronously).
I am not sure how to implement Axios Cancellation, given that our useEffect hooks are calling dispatch functions (e.g.,
getActivities()) in different files. The examples I've seen
involve fetching data within the component (rather than in context).
I am seeking assistance for #1 specifically. I would love guidance on how to accurately fetch data and store in context as global state, and then provide that context to child components, allowing them to consume/update context state without unnecessary rerendering.
Tried to only provide relevant code snippets below:
ActivityState.js -- should fetch activity data
...
const ActivityState = props => {
const initialState = {
activities: [],
isLoading: false,
isError: false
};
const HEADERS = {
'Content-Type': 'application/json',
'user_id': 1
}
const [state, dispatch] = useReducer(ActivityReducer, initialState);
const userContext = useContext(UserContext);
const getActivities = async () => {
const { loggedIn } = contactContext;
let didCancel = false; // attempts to start implementing axios cancellation
try {
const res = await axios.get(url);
dispatch({ type: GET_ACTIVITIES, payload: res.data.data.activities });
} catch (err) {
if (!didCancel) {
dispatch({ type: 'FETCH_FAILURE' });
}
}
}
const updateActivity = (path, data) => { //update context state
dispatch({ type: UPDATE_ACTIVITY, payload: { path: path, data: data } });
};
const saveActivity = () => { //send new activity data to the backend
const postData = {
actions: [{"293939": []}],
activities: state.activities
};
try {
const res = axios.post(url,{ data: postData }, { headers: HEADERS });
} catch (err) {
console.log(err);
}
}
return (
<ActivityContext.Provider
value={{
activities: state.activities,
data: state.data,
backup_data: state.backup_data,
getActivities,
updateActivity,
saveActivity,
}}
>
{props.children}
</ActivityContext.Provider>
);
};
export default ActivityState;
ActivityReducer.js -- switch statements to be dispatched by ActivityState.js
...
export default (state, action) => {
switch (action.type) {
case GET_ACTIVITIES:
return {
...state,
activities: action.payload,
isLoading: true
};
case FETCH_FAILURE:
return {
...state,
isLoading: false,
isError: true
};
case UPDATE_ACTIVITY:
const { payload: { path }, payload } = action;
const data = state;
if (!data.activities)
return { data };
const index = data.activities.findIndex(e => e.socium_tracking_number == path.id);
if(index === -1)
return { data };
_.set(data, `activities[${index}].${path.field}`, payload.data);
return {
data,
};
...
DetailsState.js -- dispatch functions to fetch details
const DetailsState = props => {
const initialState = {
details: null,
};
const [state, dispatch] = useReducer(DetailsReducer, initialState);
const getDetails = async () => {
try {
const res = await axios.get(url);
dispatch({ type: GET_DETAILS, payload: res.data.data[0].details});
}catch(err) {
console.log(err)
}
};
return (
<DetailsContext.Provider
value={{ details: state.details, getDetails }}
>
{ props.children }
</DetailsContext.Provider>
);
}
export default SchemaState;
DetailsReducer.js -- switch statement
export default (state, action) => {
switch (action.type) {
case GET_DETAILS:
return {
...state,
details: action.payload,
};
default:
return state;
}
};
ActivityTable.js -- component that consumes Activity Info
...
const ActivityTable = ({ activity }) => {
const activityContext = useContext(ActivityContext);
const { activities, filtered, getActivities } = activityContext;
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState(activities.wait_time);
// Get activity data on mount
useEffect(() => {
async function fetchData() {
await getActivities()
}
fetchData();
}, []);
...
CreateActivity.js -- component that consumes Activity and Details data
...
const CreateActivity = props => {
const activityContext = useContext(ActivityContext);
const { activities, filtered, getActivities, addActivity } = activityContext;
const detailsContext = useContext(DetailsContext);
const { details, getDetails } = detailsContext;
// Get activity and details data on mount
useEffect(() => {
async function fetchData() {
await getActivities();
await getSchema();
}
fetchData();
}, []);
...
I really tried to get smarter on these issues before approaching the SO community, so that my question(s) was more defined. But this is what I have. Happy to provide any info that I missed or clarify confusion. Thank you for your time

hapi lab AssertionError [ERR_ASSERTION]: Plugin crumb already registered

i'm not sure why i am receiving this. I am trying to create a simple test while using #hapi/crumb. i am only registering it once in my server.js.
const Path = require("path");
const hapi = require("hapi");
const inert = require("inert");
const vision = require("vision");
const Ejs = require("ejs");
const Crumb = require("#hapi/crumb");
const Blankie = require("blankie");
const Scooter = require("#hapi/scooter");
const routes = require("./routes");
// Configure the server
const server = hapi.Server({
host: "0.0.0.0",
port: process.env.PORT || 3000,
routes: {
files: {
relativeTo: Path.join(__dirname, "..", "public")
},
state: {
parse: true,
failAction: "ignore"
},
security: {
xframe: true,
noOpen: false
},
cors: {
origin: ["banglarelief.org"],
headers: ["Authorization"], // an array of strings - 'Access-Control-Allow-Headers'
exposedHeaders: ["Accept"], // an array of exposed headers - 'Access-Control-Expose-Headers',
additionalExposedHeaders: ["Accept"], // an array of additional exposed headers
maxAge: 60,
credentials: true // boolean - 'Access-Control-Allow-Credentials'
}
}
});
const plugins = async () => {
const pluginsToRegister = [
inert,
vision,
require("hapi-mobile-views"),
{ plugin: Crumb, options: { cookieOptions: { isSecure: false } } },
Scooter,
{
plugin: Blankie,
options: {} // specify options here
}
];
await server.register(pluginsToRegister);
};
const init = async () => {
await plugins();
server.state("player", {
ttl: null,
clearInvalid: true,
isSecure: false
});
server.views({
engines: { ejs: Ejs },
path: `${__dirname}/views`,
layout: "layout"
});
await server.route(routes);
return server;
};
const start = async () => {
try {
await init();
await server.start();
} catch (err) {
console.log(err);
process.exit(1);
}
};
module.exports = { init, start };
My test file is very basic and i have tried to move around where the start should be called but it keep throwing same error.
'use strict';
const Lab = require('#hapi/lab');
const { expect } = require('#hapi/code');
const { afterEach, beforeEach, describe, it } = exports.lab = Lab.script();
const { init, start } = require('../src/server');
let server = start();
describe('GET /', () => {
//let server;
//server = start();
beforeEach(async () => {
//server = start();
});
afterEach(async () => {
//await server.stop();
});
it('responds with 200', async () => {
const res = await server.inject({
method: 'get',
url: '/'
});
expect(res.statusCode).to.equal(200);
});
});
I have been following https://hapijs.com/tutorials/testing?lang=en_US
The solution seems to work if you break up your plugins function into two parts. One part will init 3rd party plugins like #Hapi/*. The other function will init your 1st party plugins that you wrote. You will only init the 3rd party plugins in your start function.
It's critical that you include { once: true } because that will prevent your error. It will only initialize the plugin once, which will prevent your error. You cannot always specify { once: true } on 3rd party plugins. Thus, we have to handle that a different way. Since we moved all the 3rd party plugins to their own function, which is invoked on start, that should prevent 3rd party plugins from causing an issue of being reinitialized.
const hapiPlugins = async () => {
const pluginsToRegister = [
inert,
vision,
require("hapi-mobile-views"),
{ plugin: Crumb, options: { cookieOptions: { isSecure: false } } },
Scooter,
{
plugin: Blankie,
options: {} // specify options here
}
];
};
const myPlugins = async () => {
await server.register([
allOfMyPlugins...
],
{
once: true //critical so that you don't re-init your plugins
});
};
const init = async () => {
server.state("player", {
ttl: null,
clearInvalid: true,
isSecure: false
});
server.views({
engines: { ejs: Ejs },
path: `${__dirname}/views`,
layout: "layout"
});
await server.route(routes);
return server;
};
const start = async () => {
try {
await hapiPlugins();
await init();
await server.start();
} catch (err) {
console.log(err);
process.exit(1);
}
};
Then, you should be able to call init in your test's before function. Use that server object to inject.