Im trying to make file upload in my api using this strategy: https://stephen-knutter.github.io/2020-02-07-nestjs-graphql-file-upload/.
Without the ValidationPipe works, but when i enable ValidationPipe this apresent error on class-transformer:
TypeError: Promise resolver undefined is not a function
at new Promise (<anonymous>)
at TransformOperationExecutor.transform (/Users/victorassis/Workspace/barreiroclub/api/node_modules/class-transformer/TransformOperationExecutor.js:117:32)
at _loop_1 (/Users/victorassis/Workspace/barreiroclub/api/node_modules/class-transformer/TransformOperationExecutor.js:235:45)
at TransformOperationExecutor.transform (/Users/victorassis/Workspace/barreiroclub/api/node_modules/class-transformer/TransformOperationExecutor.js:260:17)
at ClassTransformer.plainToClass (/Users/victorassis/Workspace/barreiroclub/api/node_modules/class-transformer/ClassTransformer.js:17:25)
at Object.plainToClass (/Users/victorassis/Workspace/barreiroclub/api/node_modules/class-transformer/index.js:20:29)
at ValidationPipe.transform (/Users/victorassis/Workspace/barreiroclub/api/node_modules/#nestjs/common/pipes/validation.pipe.js:40:39)
at /Users/victorassis/Workspace/barreiroclub/api/node_modules/#nestjs/core/pipes/pipes-consumer.js:15:33
at processTicksAndRejections (internal/process/task_queues.js:97:5)
I searched a lot, but seens like class-transformer is abandoned, and the answers was to not use ValidationPipe with upload.
Someone pass for this and found a solution?
I try to follow the example you posted above and then enable the transformation of class and I got no error as you mentioned. But I met this error before when I was trying to put the wrong type of argument in the resolver.
Below is where I setup my app bootstrap:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: extractOrigins(app.get(ConfigService).get('CORS_ORIGINS')),
});
app.useGlobalPipes(
new ValidationPipe({
transform: true,
}),
);
app.use(graphqlUploadExpress());
await app.listen(app.get(ConfigService).get('PORT') ?? 3000);
logScaffoldApp(app);
}
bootstrap();
The resolver code:
#Mutation(() => Boolean, { nullable: true })
async uploadVocabularies(
#Args({
name: 'file',
type: () => GraphQLUpload,
})
{ createReadStream, filename }: FileUpload,
) {
console.log('attachment:', filename);
const stream = createReadStream();
stream.on('data', (chunk: Buffer) => {
console.log(chunk);
});
}
And I did get the error when I try to follow another tutorial and trying to make the argument as a Promise so then the class transformer got the same error:
#Mutation(() => Boolean, { nullable: true })
async uploadVocabularies(
#Args({
name: 'file',
type: () => GraphQLUpload,
})
attachment: Promise<FileUpload>,
) {
const { filename, createReadStream } = await attachment;
console.log('attachment:', filename);
const stream = createReadStream();
stream.on('data', (chunk: Buffer) => {
console.log(chunk);
});
}
I hope this can help you and other people who viewed this post ^^
Related
I have created a small React app and I want to test it using Playwright component testing
I have 3 components: App -> ChildComponent -> ChildChildComponent
I want to render (mount) the ChildComponent directly, and make assertions on it, but when I do that, some ContextApi functions that are defined in the App in the normal flow, are now undefined as the App component is not part of the component test.
So i'v trying to render the ChildComponent together with a face ContextApi Provider and pass mocks of those undefined functions, and then I get an infinite render loop for some reason.
How can I go about this, as this use case is typical in react component test.
Here is the test with all my failed mocking attempts separated:
test.only("validate CharacterModal", async ({ page, mount }) => {
const data = ['some-mocked-irrelevant-data']
// const setCurrentCharacter = () => {};
// const setIsCharacterModalOpen = () => {};
// const setCurrentCharacterMocked = sinon.stub("setCurrentCharacter").callsFake(() => {});
// const setIsCharacterModalOpenMocked = sinon.stub("setCurrentCharacter").callsFake(() => {});
// const setCurrentCharacter = jest.fn();
// const setIsCharacterModalOpen = jest.fn();
// const setCurrentCharacter = (): void => {};
// const setIsCharacterModalOpen = (): void => {};
// const setIsCharacterModalOpen = (isCharacterModalOpen: boolean): void => {};
const AppContext = React.createContext<any>(null);
await page.route("**/users*", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(data),
});
});
const component = await mount(
<AppContext.Provider value={{ setCurrentCharacterMocked, setIsCharacterModalOpenMocked }}>
<CharacterModal />
</AppContext.Provider>
);
expect(await component.getByRole("img").count()).toEqual(4);
});
The beforeMount hook can be used for this. I recently added docs about this: https://github.com/microsoft/playwright/pull/20593/files.
// playwright/index.jsx
import { beforeMount, afterMount } from '#playwright/experimental-ct-react/hooks';
// NOTE: It's probably better to use a real context
const AppContext = React.createContext(null);
beforeMount(async ({ App, hooksConfig }) => {
if (hooksConfig?.overrides) {
return (
<AppContext.Provider value={hooksConfig.overrides}>
<App />
</AppContext.Provider>
);
}
});
// src/CharacterModal.test.jsx
import { test, expect } from '#playwright/experimental-ct-react';
import { CharacterModal } from './CharacterModal';
test('configure context through hooks config', async ({ page, mount }) => {
const component = await mount(<CharacterModal />, {
hooksConfig: { overrides: 'this is given to the context' },
});
});
I want to make API (Get & Post) requests to an API build with Yii2 using Electron.
I have tried Axion, Fetch, HTTP, request modules and all of them gave me the same error:
data: {
name: 'PHP Notice',
message: 'Undefined index: username',
code: 8,
type: 'yii\base\ErrorException',
file: 'C:\xampp\htdocs\app\controllers\ApiController.php',
line: 898,
'stack-trace': [Array]
}
Here is the code for the action I want to call:
public function actionLogin(){
if(Yii::$app->request->isPost){
Yii::$app->response->format = Response::FORMAT_JSON;
$data = Yii::$app->request->post();
$username = $data['username'];
$password = $data['password'];
$device_token = $data['device_token'];
$prefix = substr($username, 0, 3);
$model = null;
}
}
And here is the code in Electron:
axios.post('http://localhost:8080/app/api/login', {
username: 'Fred',
psssword: 'Flintstone'
})
.then(function (response) {
console.log(response);
});
For some reason, the parameters are not passing to the action.
I have tried a lot of ways and this one seems to be the simplest.
P.S. all of the way I have tried gave the same error.
I have found the solution for this issue, the way it worked was:
const login = async(username, password)=>{
const data = new URLSearchParams();
data.append('username', username);
data.append('password', password);
data.append('device_token', 'null');
await fetch(`http://localhost:8080/app/api/login`,{
method: 'post',
body: data
})
.then(res => res.json())
.then(data => {
if(data.status){
ipcRenderer.send('user:login', data.data.user_type, data.data.access_token);
}
else{
document.querySelector('#message').innerText = 'Wrong password or username';
document.querySelector('#message').style.display = 'block';
}
})
}
Here is what I tried but it's not working.
This guy is responsible for creating a composition after the meeting is ended.
app.post('/api/endMeeting', (req, res) => {
const roomSid = req.body.roomSid;
userEmail = req.body.userEmail;
const client = require('twilio')(config.twilio.apiKey, config.twilio.apiSecret, {accountSid: config.twilio.accountSid});
client.video.rooms(roomSid).update({ status: 'completed' });
client.video.compositions.create({
roomSid: roomSid,
audioSources: '*',
videoLayout: {
grid : {
video_sources: ['*']
}
},
statusCallback: `${process.env.REACT_APP_BASE_URL}/api/getMeeting`,
statusCallbackMethod: 'POST',
format: 'mp4'
}).then(() => {
// sendRecordingEmail(composition.sid, userEmail);
res.status(200).send({
message: 'success'
});
}).catch(err => {
res.status(500).send({
message: err.message
});
});
});
And this guy will send the download link of the composition to the participant when it's available.
app.post('/api/getMeeting', (req, res) => {
if (req.query.StatusCallbackEvent === 'composition-available') {
const client = require('twilio')(config.twilio.apiKey, config.twilio.apiSecret, {accountSid: config.twilio.accountSid});
const compositionSid = req.query.CompositionSid;
const uri = "https://video.twilio.com/v1/Compositions/" + compositionSid + "/Media?Ttl=3600";
client.request({
method: "GET",
uri: uri,
}).then((response) => {
const requestUrl = request(response.data.redirect_to);
sendRecordingEmail(requestUrl, userEmail);
res.status(200).send("success");
}).catch((error) => {
res.status(500).send("Error fetching /Media resource " + error);
});
}
});
I can confirm that the composition is created exactly in the Twilio console.
But it seems the status callback guy is not working and I can see the below issue.
It seems I made mistakes in using the status callback.
Please let me know what is the problem and how I can solve this.
Thank you.
Thank you very much for #philnash's help in solving this problem.👍
I solved the above issue and I can get the download link of the composition for now.
The problem was in the status callback function and I should use req.body instead of req.query because of the status callback method. (It's POST on my code.)
Here is the code that is fixed.
app.post('/api/getMeeting', (req, res) => {
if (req.body.StatusCallbackEvent === 'composition-available') {
const client = require('twilio')(config.twilio.apiKey, config.twilio.apiSecret, {accountSid: config.twilio.accountSid});
const compositionSid = req.body.CompositionSid;
const uri = "https://video.twilio.com/v1/Compositions/" + compositionSid + "/Media?Ttl=3600";
client.request({
method: "GET",
uri: uri,
}).then((response) => {
const requestUrl = response.body.redirect_to; // Getting the redirect link that user can download composition
sendRecordingEmail(requestUrl, userEmail); // Send URL via email to the user
res.status(200).send("success");
}).catch((error) => {
res.status(500).send("Error fetching /Media resource " + error);
});
} else {
res.status(204).send('compositioin is not available');
}
});
New to Apollo, so I decided to take the most simple example I found and try to work it in a slightly different way. My code can be found here.
The problem I am having is that the Subscription doesn't seem to get called when I call the Mutation createTask(). The Mutation and Subscription are defined in schema.graphql as:
type Mutation {
createTask(
text: String!
): Task
}
type Subscription {
taskCreated: Task
}
And in resolvers.js as:
Mutation: {
createTask(_, { text }) {
const task = { id: nextTaskId(), text, isComplete: false };
tasks.push(task);
pubsub.publish('taskCreated', task);
return task;
},
},
Subscription: {
taskCreated(task) {
console.log(`Subscript called for new task ID ${task.id}`);
return task;
},
},
What I am expecting to happen is that I would get a console.log in the server every time I run the following in the client:
mutation Mutation($text: String!) {
createTask(text:$text) {
id
text
isComplete
}
}
But nothing happens. What am I missing?
The subscription resolver function is called when there is actually a subscription to the GraphQL Subscription.
As you did not add a client which uses subscriptions-transport-ws and the SubscriptionClient for subscribing to your websocket and the subscription it will not work.
What you could do is add the subscription Channel to the setupFunctions of the SubscriptionManager and therein you get the value that the pubsub.publish function delivers.
Could look like this:
...
const WS_PORT = 8080;
const websocketServer = createServer((request, response) => {
response.writeHead(404);
response.end();
});
websocketServer.listen(WS_PORT, () => console.log( // eslint-disable-line no-console
`Websocket Server is now running on http://localhost:${WS_PORT}`
));
const subscriptionManager = new SubscriptionManager({
schema: executableSchema,
pubsub: pubsub,
setupFunctions: testRunChanged: (options, args) => {
return {
taskCreated: {
filter: (task) => {
console.log(task); // sould be log when the pubsub is called
return true;
}
},
};
},
,
});
subscriptionServer = new SubscriptionServer({
subscriptionManager: subscriptionManager
}, {
server: websocketServer,
path: '/',
});
...
I am wondering if we can still auto-generate Swagger API documentation for our Loopback API server if we only use the auto-discovery features outlined here:
https://docs.strongloop.com/display/public/LB/Discovering+models+from+relational+databases
does anyone know if it's possible? If we use autodiscovery, I somehow doubt that any .json files for our models will get written to our server project, and that will make generating docs difficult.
Turns out yes it is possible, and the way to do that is to write the models-x.json files out for all models with a script, and then start the server after the script has finished!
https://docs.strongloop.com/display/public/LB/Database+discovery+API
this is standard practice for auto-discovery, here is my code that accomplishes this:
const loopback = require('loopback');
const fs = require('fs');
const path = require('path');
const async = require('async');
var ds = loopback.createDataSource('postgresql', {
'host': 'localhost',
'port': 5432,
'database': 'foo',
'username': 'bar',
'password': 'baz'
});
ds.discoverModelDefinitions(function (err, models) {
async.each(models, function (def, cb) {
ds.discoverSchema(def.name, null, function (err, schema) {
if (err) {
console.error(err.stack || err);
cb(err);
}
else {
fs.writeFile(path.resolve(__dirname, 'server/models', def.name + '.json'),
JSON.stringify(schema), {}, cb);
}
});
}, function (err) {
if (err) {
console.log(err.stack || err);
process.exit(1);
}
else {
console.log(' => Successfully wrote model data.');
process.exit(0);
}
});
});