hapi lab AssertionError [ERR_ASSERTION]: Plugin crumb already registered - hapi

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.

Related

Error while Uploading Image to Mongodb using Gridfs and Graphql

Im trying to upload an image to mogodb using graphql and gridfs. When trying to do i'm facing a error :
" JSON Parse error: Unexpected identifier "This" "
I'm not sure what I've done wrong in the code.
Can anyone help me figure out where I've gone wrong in the implementation
This is the part for uploading Image
const selectProfilePic = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [2, 3],
quality: 1,
});
handleImagePicked(result);
};
const handleImagePicked = async (result: ImagePicker.ImagePickerResult) => {
try {
if (result.cancelled) {
alert("Upload cancelled");
return;
} else {
console.log("In HERE ::: 76");
const lastIndex = result.uri.lastIndexOf("/") + 1;
console.log(result);
const file = new ReactNativeFile({
uri: result.uri,
name: result.uri.substring(lastIndex),
type: "image/png",
});
setAvatar(result.uri);
console.log(file); // This result is getting printed.
await singleUpload({ // I think the Upload function is not getting called.
variables: {
file,
},
});
}
} catch (e) {
console.log(e);
alert("Upload failed");
}
};
Resolver Function
singleUpload: async (_, { file }, context) => {
const {db, user, hhhhhh, gfs} = context;
console.log(gfs);
const res = uploadFn({ file },gfs);
console.log(res);
Apollo.tsx File
const uploadLink = createUploadLink({
uri: CLIENT_HTTP_URI,
});
// splitLink is defined here
export const client = new ApolloClient({
link: ApolloLink.from([authLink, splitLink]),
cache: new InMemoryCache(),
});
Have created StorageEngine.js File with this code
const storage = new GridFsStorage({
url: DB_URI,
file: (req, file) => {
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if(err) { return reject(err); }
const fileName = buf.toString('hex') + path.extname(file.originalname);
const fileInfo = {
fileName,
bucketName: 'uploads'
};
resolve(fileInfo);
});
});
}
});
const upload = multer({storage});
const uploadFn = async ({file}, bucket) => {
console.log(file);
const { createReadStream, name, type, encoding } = await file;
const uploadStream = bucket.openUploadStream(name, {
contentType: type
});
// console.log(uploadStream);
return new Promise((resolve, reject) => {
createReadStream()
.pipe(uploadStream)
.on('error', reject)
.on('finish', () => {
console.log(uploadStream.id);
resolve(uploadStream.id);
});
});
}

Apollo Server Express v4 GraphQL this.findOneById is not a function

I have the following ApolloServer (v4)
import { MongoDataSource } from 'apollo-datasource-mongodb'
export default class LoaderAsset extends MongoDataSource {
async getAsset(assetId) {
return this.findOneById(assetId) // ERROR IS HERE
}
}
async function startApolloServer(app) {
const httpServer = http.createServer(app);
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })]
});
await server.start();
app.use(
'/graphql',
cors(),
bodyParser.json(),
expressMiddleware(server, {
context: async ({ req }) => {
return {
dataSources: {
loaderAsset: new LoaderAsset(modelAsset),
}
}
},
}),
);
const port = config.get('Port') || 8081;
await new Promise(resolve => httpServer.listen({ port }, resolve));
}
when I run graphql and send one assetId, everything is working till I get following error:
this.findOneById is not a function
By the way (this) has collection and model objects but not any methods.
is it because apollo-datasource-mongodb is not compatible with the new version of apollo server v4?
dataSources in v3 were as follows:
dataSources: () => ({
users: new Users(client.db().collection('users'))
// OR
// users: new Users(UserModel)
})
but in the new version dataSources is inside the context
Maybe the issue is because of this change.
Credit to: https://github.com/systemkrash on https://github.com/GraphQLGuide/apollo-datasource-mongodb/issues/114
what i did is i override my datasource class so I can call the initialize method inside in the MongoDatasource class. Now it works for me.
import { MongoDataSource } from 'apollo-datasource-mongodb';
class ReceptionDataSource extends MongoDataSource {
constructor({ collection, cache }) {
super(collection);
super.initialize({ context: this.context, cache });
}
async getReception(receptionId) {
return await this.findOneById(receptionId);
}
}
export default ReceptionDataSource;
then in my context
async function startApolloServer(app) {
const httpServer = http.createServer(app);
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })]
});
await server.start();
app.use(
'/graphql',
cors(),
bodyParser.json(),
expressMiddleware(server, {
context: async ({ req }) => {
const { cache } = server
return {
dataSources: {
loaderAsset: new ReceptionDataSource({collection: ReceptionModel, cache}),
}
}
},
}),
);
const port = config.get('Port') || 8081;
await new Promise(resolve => httpServer.listen({ port }, resolve));
}

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);
}

Cannot read property 'find' of inherited method in unit tests

im having an issue trying to run an e2e test for my NestJS application tha uses a mongodb-memory-server to run mongo in memory
my e2e test looks like this
describe('UsersController.e2e', () => {
let app: INestApplication;
let module: TestingModule;
const mongod = new MongoMemoryServer();
beforeAll(async () => {
const port = await mongod.getPort();
const database = await mongod.getDbName();
module = await Test.createTestingModule({
providers: [UserRepository, UserService],
controllers: [UserController],
imports: [TypeOrmModule.forRootAsync({
useFactory: () => {
return {
type: 'mongodb',
host: '127.0.0.1',
port,
database,
entities: [__dirname + '../../../**/*.entity{.ts,.js}'],
} as TypeOrmModuleOptions;
},
}),
TypeOrmModule.forFeature([User])
]
}).compile();
app = module.createNestApplication();
await app.init();
});
afterAll(async () => {
await module.close();
await app.close();
});
describe('GET /users', () => {
it('should return a collection of user resources', async () => {
const { body } = await supertest
.agent(app.getHttpServer())
.get('/users')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200);
expect(body).toEqual(userCollectionMock);
});
});
});
when running the test it throws a 500 error
Cannot read property 'find' of undefined
TypeError: Cannot read property 'find' of undefined
at UserRepository.Object.<anonymous>.MongoRepository.find (src/repository/MongoRepository.ts:77:29)
at UserRepository.index (src/modules/user/repositories/user.repository.ts:12:20)
the repository class looks like
export class UserRepository extends MongoRepository<User> implements IResourceRepository<User> {
index(): Promise<User[]> {
return this.find();
}
}
the extension of MongoRepository provides find() https://github.com/typeorm/typeorm/blob/master/src/repository/MongoRepository.ts#L76
So it perplexes me as to why it is undefined!
I know this is a month old, but I just ran into this issue and resolved it.
I'm using mongoose + typegoose with mongodb-memory-server.
import { MongoMemoryServer } from 'mongodb-memory-server';
describe('Auth', () => {
let app: INestApplication;
let mongoServer: MongoMemoryServer;
beforeAll(async () => {
// Set up database
mongoServer = new MongoMemoryServer();
const mongoUri = await mongoServer.getUri();
const moduleRef = await Test.createTestingModule({
imports: [
...
TypegooseModule.forRoot(mongoUri, {
useNewUrlParser: true,
useUnifiedTopology: true,
}),
...
],
controllers: [...],
providers: [...],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
afterAll(async () => {
await mongoServer.stop();
});
});
The main mistake I was making was that I had separately overwritten my User Typegoose injection.
providers: [
// THIS WAS MY MISTAKE!
{
provide: getModelToken('User'),
useValue: mockUserModel,
},
],
I know this is not a complete answer to your question, but I hope this might help you.

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