NestJS upload multiple files using GraphQL - file-upload

I tried to upload multiple files using Nestjs Graphql but i can't ..
this code works fine with controller (Rest) exactly in https://docs.nestjs.com/techniques/file-upload
#Post('upload')
#UseInterceptors(FilesInterceptor('files',saveImageToStorage))
uploadImages(#UploadedFiles() files:Array<Express.Multer.File>, #Req() req:Request):any{
return ;
}
but it can't work with Graphql may be because (FileInterceptor) can not work with graphql , how i impelment this code or this way with graphql to upload multiple images?
note: I tried many ways to upload multiple images using nest graph(with multer and with graphql-upload) but all ways failed !
image-storage.ts
import { diskStorage } from "multer";
import { generate } from 'shortid';
import * as fs from 'fs';
//const FileType = require('file-type');
import path = require('path');
type validFileExtension = 'png' | 'jpg' | 'jpeg';
type validMimeType = 'image/png' | 'image/jpg' | 'image/jpeg';
const validFileExtensions: validFileExtension[] = ['png' , 'jpg' , 'jpeg'];
const validMimeTypes: validMimeType[] = ['image/png' , 'image/jpg' , 'image/jpeg'];
export const saveImageToStorage = {
storage:diskStorage({
destination:'./images',
filename:(req,file,cb)=>{
const fileExtension:string = path.extname(file.originalname);
const fileName:string = generate() + fileExtension
cb(null,fileName)
}
}),
fileFilter: (req, file, cb) => {
const allowedMimeTypes: validMimeType[] = validMimeTypes;
allowedMimeTypes.includes(file.mimetype) ? cb(null,true) : cb(null,false)
}
}
export const removeFile = (fullFilePath:string):void => {
try{
fs.unlinkSync(fullFilePath);
} catch(err) {
console.log(err);
}
}

Could you please provide the AppModule code ?
Have you tried this :
import { graphqlUploadExpress } from 'graphql-upload';
...
....
....
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(graphqlUploadExpress()).forRoutes('graphql');
}
}
and in your GraphQLModule parameters :
uploads: false

Related

Missing createReadStream as a argument from passed file

Im trying to upload file from react application , there are 3arguments: filename, mimetype and encoding . After the file is uploaded all three are passed to the server but there is no parameter createReadStream.
$ node -v
v16.13.2
I've tried so far to send the file to the apollo-server.
my Queries are following:
export const UPLOAD = gql`
mutation Upload($uploadFile: Upload) {
fileUpload {
uploadFile(file: $uploadFile) {
filename
mimetype
encoding
}
}
}
`;
import { useMutation } from '#apollo/client'
import React from 'react'
import { UPLOAD } from './queries/Queries'
const UploadCV = () => {
const [UploadCV, { data, error,refe }]= useMutation(UPLOAD,{
context:{clientName:'upload'},
onComplete:data=>console.log(data)
})
const handleUpload = (e)=>{
console.log(e.target.files[0])
const file = e.target.files[0]
// console.log(typeof file.name)
// console.log(typeof file.type)
// console.log(typeof String(file.size))
if(!file)return
UploadCV({
variables:{
uploadFile: {
filename: file.name,
mimetype: file.type,
encoding: String(file.size),
}
}
})
}
return (
<div>
<input type="file"
onChange={handleUpload}
id="avatar" name="avatar"
accept="image/png, image/jpeg, application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document"
/>
</div>
)
}
export default UploadCV
Till now its fine but once its hit server start the problem.
const Upload = require("graphql-upload-minimal").GraphQLUpload
module.exports = {
fileUpload: {
Upload,
async uploadFile({ file:{ filename,mimetype, encoding,createReadStream} }) {
console.log('FileName:',filename , 'MiemeType:', mimetype,'Encoding:', encoding,'CreateReadStream:',createReadStream)
`
},
},
};
That is the output of the sended file.
My schema
const { buildSchema } = require('graphql');
const uploadFile = require('./uploadFile');
module.exports = buildSchema(`
scalar Upload
${uploadFile}
type Mutation{
fileUpload:UploadFile
}
type Query{
getCVs:getAllCV
}
`);
module.exports = `
type File{
filename:String
mimetype:String
encoding:String
}
type UploadFile{
uploadFile(file:Upload):File
}
type getAllCV{
getAllCV:[File]
}`

Fetching API for Articles with NextJS and Strapi

I would like some help on an API issue.
I have been trying to link each Article page based on the content I have created in Strapi CMS on my local server.
The API endpoint that I manage to gather data is from 'http://localhost:1337/api/articles?populate=*'.
Here is my code:
// lib/api.js
export class ApiError extends Error {
constructor(url, status) {
super(`'${url}' returned ${status}`);
if(Error.captureStackTrace) {
Error.captureStackTrace(this, ApiError);
}
this.name = 'ApiError';
this.status = status;
}
}
export async function fetchJson(url, options) {
const response = await fetch(url, options);
if(!response.ok) {
throw new ApiError(url, response.status);
}
return await response.json();
}
// lib/articles.js
import { fetchJson } from "./api";
const API_URL = process.env.API_URL;
// Gets a single article
export async function getArticle(id) {
const article = await fetchJson(`${API_URL}/api/article/${id}`);
return stripArticle(article);
}
// Gets all articles
export async function getArticles() {
const articles = await fetchJson(`${API_URL}/api/articles`);
return articles.map(stripArticle);
}
function stripArticle(article) {
return {
id: article.id,
title: article.attributes.Title,
content: article.attributes.Content,
pictureUrl: API_URL + article.attributes.Photo.formats.thumbnail.url,
}
}
Article Page:
//article/[id].js
import Page from "../../components/Page";
import { getArticle, getArticles } from "../../lib/articles";
import ReactMarkdown from 'react-markdown';
import Moment from 'react-moment';
export async function getStaticProps({ params }) {
const article = await getArticle(params.id)
return {
props: { article },
unstable_revalidate: 1,
}
}
export default function Article({ article }) {
return (
<Page title={article.Title}>
<ReactMarkdown source={article.Content} />
<p>
<Moment from="MM Do YYYY">{article.CreatedAt}</Moment>
</p>
</Page>
)
}
export async function getStaticPaths() {
const articles = await getArticles()
return {
paths: articles.map((article) => ({
params: { id: article.id.toString() }, // Number convert to string
})),
fallback: 'blocking', // What if error. Client is blocked, until new page is ready.
};
}
I would get an error: TypeError: articles.map is not a function.
If there is a better way to format and write the code, do let me know as I have been trying to find which is best.
Thanks for the help in advance.

vue3 testing library - How to use globalProperties in tests

I am new to Vue and followed the recommendation to use vue testing library. The only issue is I can't seem to find a way to inject my code into globalProperties in render function.
Does anyone know of an example where I can inject or mock it out?
main.js
app.config.globalProperties.$globals = globalMethods
...
const app = createApp(App)
app.config.globalProperties.$globals = globalMethods
app.config.globalProperties.$globalVars = globalVars
app.component("font-awesome-icon", fontawesome)
app.use(applicationStore);
app.use (Hotjar, hotjarConfig)
app.use(i18n)
app.use(router)
app.mount('#app')
From my vue component in create I am able to call
Component.vue
let formatedObj = this.$globals.maskValues(this.inputValue, this.inputType, this);
...
,
created() {
let formatedObj = this.$globals.maskValues(this.inputValue, this.inputType, this);
this.myInputValue = formatedObj.formatedString;
this.formatedCharacterCount = formatedObj.formatedCharacterCount;
this.prevValue = this.myInputValue;
},
...
tesst.spec.js
import { render } from '#testing-library/vue'
import FormatedNumericInput from '#/components/Component.vue'
import {globalMethods} from'#/config/global-methods'
const label = 'Price'
const initSettings = {
props: {
inputId: 'testInputId1',
labelTxt: label
}
};
beforeEach(() => {
});
test('a simple string that defines your test', () => {
const { getByLabelText } = render(FormatedNumericInput, initSettings)
const input = getByLabelText(label)
// testing logic
expect(input != null).toBe(true)
expect(FormatedNumericInput != null).toBe(true)
})
** ERROR **
TypeError: Cannot read property 'maskValues' of undefined
85 | },
86 | created() {
> 87 | let formatedObj = this.$globals.maskValues(this.inputValue, this.inputType, this);
| ^
88 | this.myInputValue = formatedObj.formatedString;
89 | this.formatedCharacterCount = formatedObj.formatedCharacterCount;
90 | this.prevValue = this.myInputValue;
at Proxy.created (src/components/FormatedNumericInput.vue:87:37)
The second argument of render() is passed to #vue/test-utils mount(), so you could include the global.mocks mounting option to mock $globals.maskValues:
const { getByLabelText } = render(FormatedNumericInput, {
...initSettings,
global: {
mocks: {
$globals: {
maskValues: (inputValue, inputType) => {
const formatedString = globalFormatValue(inputValue) // declared elsewhere
return {
formatedString,
formatedCharacterCount: formatedString.length,
}
}
}
}
}
})
This is my solution in actual Vue3/Vite/Vitest environment, I set some mocks globally, so I don't need to in every test suite.
// vitest.config.ts
import { mergeConfig } from 'vite';
import { defineConfig } from 'vitest/config';
import viteConfig from './vite.config';
export default defineConfig(
mergeConfig(viteConfig, { // extending app vite config
test: {
setupFiles: ['tests/unit.setup.ts'],
environment: 'jsdom',
}
})
);
// tests/unit.setup.ts
import { config } from "#vue/test-utils"
config.global.mocks = {
$t: tKey => tKey; // just return translation key
};
so for you it will be something like
config.global.mocks = {
$globals: {
maskValues: (inputValue, inputType) => {
// ...implementation
return {
formatedString,
formatedCharacterCount,
}
}
}
}

How to parse serverless.yml file in script

I need to read the serverless.yml config for use in some test mocks.
The following worked until a recent change:
const serverless = new Serverless()
await serverless.init()
const service = await serverless.variables.populateService()
How does one read the file now? There is an astounding lack of documentation in regards to using serverless progamically.
Well I ended up taking some code from the AppSync emulator package. I am not sure it covers it does a full parsing but it does the job for me.
import Serverless from 'serverless'
import path from 'path'
import fs from 'fs'
class ConfigServerless extends Serverless {
async getConfig(servicePath) {
this.processedInput = {
commands: [],
options: { stage: 'dev' }
}
this.config.servicePath = servicePath
this.pluginManager.setCliOptions(this.processedInput.options)
this.pluginManager.setCliCommands(this.processedInput.commands)
await this.service.load(this.processedInput)
this.pluginManager.validateCommand(this.processedInput.commands)
return this.variables
.populateService(this.pluginManager.cliOptions)
.then(() => {
this.service.mergeArrays()
this.service.setFunctionNames(this.processedInput.options)
this.service.validate()
})
}
}
const normalizeResources = config => {
if (!config.resources) {
return config.resources
}
if (!config.resources.Resources) {
return {}
}
if (!Array.isArray(config.resources.Resources)) {
return config.resources
}
const newResources = config.resources.Resources.reduce(
(sum, { Resources, Outputs = {} }) => ({
...sum,
...Resources,
Outputs: {
...(sum.Outputs || {}),
...Outputs
}
}),
{}
)
return {
Resources: newResources
}
}
export async function loadServerlessConfig(cwd = process.cwd()) {
const stat = fs.statSync(cwd)
if (!stat.isDirectory()) {
cwd = path.dirname(cwd)
}
const serverless = new ConfigServerless()
await serverless.getConfig(cwd)
const { service: config } = serverless
const { custom = {} } = config
const output = {
...config,
custom: {
...custom
},
resources: normalizeResources(config)
}
return output
}

How to use a mocked data with react-apollo for tests

I'm using react-apollo to build a client that consumes a GraphQL API, however, I'm very stuck on testing. What I want is to mock the server so I can easily test the application without needing to make network calls.
I've found some pointers on how to mock the server:
https://dev-blog.apollodata.com/mocking-your-server-with-just-one-line-of-code-692feda6e9cd
http://dev.apollodata.com/tools/graphql-tools/mocking.html#addMockFunctionsToSchema
But there isn't really an example on how to use this mocked server in my app tests to avoid hitting the server.
My goal is to setup integration tests to assert that the app is actually working:
describe('Profile feature', () => {
beforeAll(() => {
store = setupStore();
app = mount(
<ApolloProvider store={store} client={apolloClient}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</ApolloProvider>
);
});
});
The store is using Redux and the client is being created like this:
const networkInterface = createNetworkInterface({
uri: process.env.REACT_APP_API_URL
});
export const apolloClient = new ApolloClient({
networkInterface
});
How can I use a mocked server with graphql-tools here instead of the actual API?
I found 2 different ways of creating mocked data for apollo-client queries:
The first is to use graphql-tools to create a mocked server based on your backend schema, in order to connect this mocked server with your tests it's possible to create a mockNetworkInterface like this:
const { mockServer } = require("graphql-tools");
const { print } = require("graphql/language/printer");
class MockNetworkInterface {
constructor(schema, mocks = {}) {
if (schema === undefined) {
throw new Error('Cannot create Mock Api without specifying a schema');
}
this.mockServer = mockServer(schema, mocks);
}
query(request) {
return this.mockServer.query(print(request.query), request.variables);
}
}
You can pass this network interface to the ApolloClient component and it should work just fine!
Having this setup requires to have your API schema up to date in your client, so I found it a bit of a pain to do.
Another way of doing this is using the mockNetworkInterface provided by apollo-client/test-utils
You can use it this way:
import App from './App';
import { UserMock, PublicationMock } from '../__mocks__/data';
import { mockNetworkInterface } from 'react-apollo/test-utils';
import ApolloClient from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
// We will be using here the exact same Query defined in our components
// We will provide a custom result or a custom error
const GraphQLMocks = [
{
request: {
query: UserProfileQuery,
variables: {}
},
result: {
data: {
current_user: UserMock
}
}
}
];
// To set it up we pass the mocks to the mockNetworkInterface
const setupTests = () => {
const networkInterface = mockNetworkInterface.apply(null, GraphQLMocks);
const client = new ApolloClient({ networkInterface, addTypename: false });
const wrapper = mount(
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);
return {
store,
wrapper
};
};
// Then the tests look like this
describe('Profile feature', () => {
test('Profile view should render User details', async () => {
const { wrapper, store } = setupTests();
const waitFor = createWaitForElement('.profile');
await waitFor(wrapper);
const tag = wrapper.find('.profile-username');
expect(tag.text()).toEqual(`${UserMock.first_name} ${UserMock.last_name}`);
});
});
It is important to pass addTypename: false to the ApolloClient instance, otherwise you will need to add __typename to all your queries manually.
You can inspect the implementation of the mockNetworkInterface here: https://github.com/apollographql/apollo-test-utils/blob/master/src/mocks/mockNetworkInterface.ts
You can also use MockedProvider, which makes it even simpler.
withPersons.js
import { gql, graphql } from 'react-apollo'
export const PERSONS_QUERY = gql`
query personsQuery {
persons {
name
city
}
}
`
export const withPersons = graphql(PERSONS_QUERY)
withPersons.test.js
/* eslint-disable react/prop-types */
import React, { Component } from 'react'
import { MockedProvider } from 'react-apollo/test-utils'
import { withPersons, PERSONS_QUERY } from '../withPersons'
it('withPersons', (done) => {
const mockedData = {
persons: [
{
name: 'John',
city: 'Liverpool',
},
{
name: 'Frank',
city: 'San Diego',
},
],
}
const variables = { cache: false }
class Dummy extends Component {
componentDidMount() {
const { loading, persons } = this.props.data
expect(loading).toBe(true)
expect(persons).toBe(undefined)
}
componentWillReceiveProps(nextProps) {
const { loading, persons } = nextProps.data
expect(loading).toBe(false)
expect(persons).toEqual(mockedData.persons)
done()
}
render() {
return null
}
}
const DummyWithPersons = withPersons(Dummy)
mount(
<MockedProvider
removeTypename
mocks={[
{
request: { query: PERSONS_QUERY, variables },
result: { data: mockedData } },
]}
>
<DummyWithPersons />
</MockedProvider>,
)
})
Note: By using a Dummy component you just test your graphql() Queries and Mutations and the way you have configured them (options, props, skip, variables, etc.) So you don't mount your actual React components. It's better to test those in their 'unconnected' state.
I wrote up a blog post a while that might be helpful: http://blog.dideric.is/2018/03/18/Testing-apollo-containers/
Apollo has something called LinkSchema that makes the first approach Carlos mentioned a lot easier. It still takes some setup, but I think it's worth it. If you're creating responses manually, you have to worry a lot more about keeping your tests up to date/getting false positives when the schema changes and you haven't accounted for it in your code.