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
}
Related
here's my current setup that is resulting in TypeError: undefined is not a function
import client from 'GraphQl/apolloClient'
import { ADD_POST_IMAGE } from 'GraphQl/News/Mutations'
function MyCustomUploadAdapterPlugin(editor) {
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
return new MyUploadAdapter(loader)
}
}
class MyUploadAdapter {
constructor(props) {
// CKEditor 5's FileLoader instance.
this.loader = props
this.mutation = client.mutate({ mutation: ADD_POST_IMAGE })
}
// Starts the upload process.
upload() {
return new Promise((resolve, reject) => {
this._sendRequest()
})
}
// Prepares the data and sends the request.
_sendRequest() {
const [addPost, { error }] = this.mutation
this.loader.file.then(async (result) => {
const { data: response } = await addPost({
variables: { data: { image: result } },
})
console.log(response)
})
}
}
export default MyCustomUploadAdapterPlugin
i'm trying to setup a custom upload adapter for React CKEditor plugin 5.
since i have a graphql backend, i plan to use mutations for upload.
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.
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
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,
}
}
}
}
I am a subscription setup but onNext is not getting triggered I am not sure why since this is my first time implementing subscription and docs was not much help with the issue.
Here are the code implementations:
import {
graphql,
requestSubscription
} from 'react-relay'
import environment from '../network';
const subscription = graphql`
subscription chatCreatedSubscription{
chatCreated{
id
initiate_time
update_time
support_id
category_id
email
name
}
}
`;
function chatCreated(callback) {
const variables = {};
requestSubscription(environment, {
subscription,
variables,
onNext: () => {
console.log("onNext");
callback()
},
updater: () => {
console.log("updater");
}
});
}
module.exports = chatCreated;
and here is my network for the subscription
import { Environment, Network, RecordSource, Store } from "relay-runtime";
import Expo from "expo";
import { SubscriptionClient } from "subscriptions-transport-ws";
import { WebSocketLink } from 'apollo-link-ws';
import { execute } from 'apollo-link';
import accessHelper from "../helper/accessToken";
const networkSubscriptions = async (operation, variables) => {
let token = await accessHelper();
if (token != null || token != undefined) {
const subscriptionClient = new SubscriptionClient("ws://localhost:3000/graphql",
{
reconnect: true,
connectionParams: {
Authorization: token,
},
});
execute(new WebSocketLink(subscriptionClient), {
query: operation.text,
variables,
});
}
}
const network = Network.create(fetchQuery, networkSubscriptions);
const store = new Store(new RecordSource());
const environment = new Environment({
network,
store
});
export default environment;
the subscription is called in a componentDidMount method on a component it executes but the onNext method inside the subscription is never triggered when new information is added to what the subscription is listening to.
so i figured out that my issue was the network js not being setup properly and the version of subscription-transport-ws. i added version 0.8.3 of the package and made the following changes to my network file:
const networkSubscriptions = async (config, variables, cacheConfig, observer) => {
const query = config.text;
let token = await accessHelper();
if (token != null || token != undefined) {
const subscriptionClient = new SubscriptionClient(`ws://${api}/graphql`,
{
reconnect: true,
connectionParams: {
Authorization: token,
},
});
subscriptionClient.subscribe({ query, variables }, (error, result) => {
observer.onNext({ data: result })
})
return {
dispose: subscriptionClient.unsubscribe
};
}
}
i hope this helps you if you get stuck with the same issue as mine.