Unable to parse class method decorator in a Babel plugin - babeljs

I'm writing a Babel plugin that manipulates the AST node related to a specific decorator. I'm traversing the AST but for some reason, my plugin doesn't detect the method decorator - node.decorators is always null when the visitor visits a node.
This is the plugin:
import { ClassMethod, Decorator } from '#babel/types';
import { get } from 'lodash';
import { NodePath, PluginObj } from '#babel/core';
const providerArgumentsTransformer = (): PluginObj => ({
visitor: {
ClassMethod({ node, parent }: NodePath<ClassMethod>) {
const decorator = getProviderDecorator(node.decorators); // <- node.decorators is always null
if (getDecoratorName(decorator) === 'Provides') {
console.log('Success');
}
},
},
});
function getProviderDecorator(decorators: Array<Decorator> | undefined | null): Decorator | undefined {
return decorators?.find((decorator) => get(decorator, 'expression.callee.name') === 'Provides');
}
function getDecoratorName(decorator?: Decorator): string | undefined {
return get(decorator, 'expression.callee.name');
}
export default providerArgumentsTransformer;
I'm testing the decorator as follows:
import { PluginObj } from '#babel/core';
import * as babel from '#babel/core';
import providerArgumentsTransformer from './providerArgumentsTransformer';
const code = `class MainGraph {
Provides(clazz, propertyKey, descriptor) { }
#Provides()
someString(stringProvider) {
return stringProvider.theString;
}
}`;
describe('Provider Arguments Transformer', () => {
const uut: PluginObj = providerArgumentsTransformer();
it('Exposes transformer', () => {
babel.transformSync(code, {
plugins: [
['#babel/plugin-proposal-decorators', { legacy: true }],
['#babel/plugin-proposal-class-properties', { legacy: true }],
[uut, { legacy: true }],
],
configFile: false,
});
});
});
I wonder if the issue is related to how babel.transformSync is used or perhaps the visitor is not configured properly.

Turns out the decorators were missing because #babel/plugin-proposal-decorators clears the decorators when it traverses the AST.
In order to visit the node before #babel/plugin-proposal-decorators I had to modify my visitor a bit. This approach should probably be optimized by visiting ClassBody or ClassExpression instead of Program.
const providerArgumentsTransformer: PluginObj = {
visitor: {
Program(path: NodePath<Program>) {
path.traverse(internalVisitor);
},
},
};
const internalVisitor = {
ClassMethod: {
enter({ node }: NodePath<ClassMethod>) {
// node.decorators are not null anymore
},
},
};

Related

ConnectorError Prisma with MongoDB

I am developing an API with Nestjs and MongoDB. Well sometimes I have this problem. It doesn't always happen and that's what I have to solve because this error rarely happens.
This is the error:
{
"status": "error",
"message": "\nInvalid this.prisma.people.findMany() invocation in\nC:\ima\empresas\datec\api-official\src\modules\Events\services\utils.service.ts:57: 28\n\n 54 }),\n 55 \n 56 // Get attendees by id\n→ 57 this.prisma.people.findMany(\nError in batch request 3: Error occurred during query execution:\nConnectorError(ConnectorError { user_facing_error: None, kind: RawDatabaseError { code: "unknown", message: "An existing connection was forced to be terminated by the remote host. (os error 10054)" } })"
}
I really can't find much information to solve this error. I have followed the steps in the documentation on Nestjs when using prisma with nestjs. I need help to solve this error, since it is essential to find a solution as soon as possible. Thanks so much for read this question
UPDATE
The Prisma service
// Librarys
import { Injectable } from '#nestjs/common'
import { PrismaClient } from '#prisma/client'
// Interfaces
import { INestApplication, OnModuleInit, OnModuleDestroy } from '#nestjs/common'
#Injectable()
export class PrismaService
extends PrismaClient
implements OnModuleInit, OnModuleDestroy
{
async onModuleInit() {
await this.$connect()
}
async onModuleDestroy() {
await this.$disconnect()
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close()
})
}
}
File that starts the application
//Libraries
import { NestFactory } from '#nestjs/core'
import { ValidationPipe } from '#nestjs/common'
import { SwaggerModule, DocumentBuilder } from '#nestjs/swagger'
import { useContainer } from 'class-validator'
// Modules
import { MainModule } from './main.module'
// config
import pk from '#root/package.json'
import { corstOptions } from '#config/cors'
import { PORT, APP_NAME, PUBLIC_URL } from '#config/env'
// Exceptions
import { HttpExceptionFilter } from '#utils/HttpExceptionFilter'
import { PrismaService } from '#root/src/services/prisma'
async function bootstrap() {
// Create application
const app = await NestFactory.create(MainModule)
// Enable cors
app.enableCors(corstOptions)
// Use global 'api' prefix, all calls will come after '/api/*'
app.setGlobalPrefix('api')
// Globally define custom response
app.useGlobalFilters(new HttpExceptionFilter())
// Enable prism on custom validations
useContainer(app.select(MainModule), { fallbackOnErrors: true })
// Get service from primsa and enable 'shutdowns'
const prismaService = app.get(PrismaService)
await prismaService.enableShutdownHooks(app)
// Use 'pipe' validation to validate the 'body' structure
app.useGlobalPipes(
newValidationPipe({
whitelist: true,
transform: true,
forbidUnknownValues: true,
forbidNonWhitelisted: true,
transformOptions: { enableImplicitConversion: true }
})
)
// Create API documentation
const config = new DocumentBuilder()
.addBearerAuth()
.setTitle(`API | ${APP_NAME}`)
.setContact(pk.author.name, pk.author.url, pk.author.email)
.setDescription(pk.description)
.setVersion(pk.version)
.build()
const document = SwaggerModule.createDocument(app, config)
SwaggerModule.setup('/', app, document)
// Listen to the application on the PORT defined in the environment variables
await app.listen(PORT || 0, () => {
// eslint-disable-next-line no-console
console.log(
'\x1b[33m%s\x1b[0m',
`[INFO] The server has been started at '${PUBLIC_URL}'`
)
})
}
bootstrap() // Run application
utils.service of Event module
// Librarys
import { Prisma } from '#prisma/client'
import { Inject, Injectable } from '#nestjs/common'
// Services
import { PrismaService } from '#services/prisma'
// DTO
import { EventBodyDTO } from '#modules/Events/events.dto'
// Arguments
import { EventsArgs } from '#modules/Events/events.args'
#Injectable()
export class UtilsService {
#Inject(PrismaService)
private readonly prisma: PrismaService
private readonly selects = {
'id.title': { id: true, title: true },
'id.fullname': { id: true, fullname: true },
'id.title.attributes': { id: true, title: true, attributes: true },
'id.code.description.attributes': {
id: true,
code: true,
description: true,
attributes: true
}
}
/**
* Get reminders, types of activities, case, client and attendees of an event
* #param {EventBodyDTO} payload Event data
*/
async getEventFields(payload: EventBodyDTO) {
const [activityType, eventCase, client, assistants, reminders] =
await Promise.all([
// Get an activity type by id
this.prisma.parameters.findUnique({
select: this.selects['id.title'],
where: { id: payload.activityType }
}),
// Get a case by id
this.prisma.expedients.findUnique({
where: { id: payload.case },
select: this.selects['id.code.description.attributes']
}),
// Get a person by id
this.prisma.people.findFirst({
select: this.selects['id.fullname'],
where: { isClient: true, id: payload.client }
}),
// Get attendees by id
this.prisma.people.findMany({
select: this.selects['id.fullname'],
where: {
isEmployee: true,
id: { in: payload.assistants }
}
}),
// Get reminders by id
this.prisma.parameters.findMany({
select: this.selects['id.title.attributes'],
where: {
id: { in: payload.reminders }
}
})
])
return {
reminders: reminders,
assistants: assistants,
client: client === null ? {} : client,
case: eventCase === null ? {} : eventCase,
activityType: activityType === null ? {} : activityType
}
/**
* Create filters to filter by customer or event attendees
* #param {EventsArgs} args Arguments to filter
* #returns {Promise<Prisma.EventsFindManyArgs['where']>} Returns an object with filters
*/
async createEventFilters(
args: EventsArgs
): Promise<Prisma.EventsFindManyArgs['where']> {
const filters: Prism.EventsFindManyArgs['where'] = {
userId: args.user
}
// Filter by customer
if (typeof args.client === 'string') {
const clients = await this.prisma.people.findMany({
where: {
isClient: true,
fullname: {
mode: 'insensitive',
contains: args.client
}
},
select: {
id: true,
fullname: true
}
})
filters.OR = []
for (const client of clients) {
filters.OR.push({
client: { equals: client }
})
}
}
// Filter by attendees
if (Array.isArray(args.assistants)) {
const assistants = await this.prisma.people.findMany({
where: {
isEmployee: true,
id: {
in: args.assistants
}
},
select: {
id: true,
fullname: true
}
})
if (!Array.isArray(filters.OR)) {
filters.OR = []
}
filters.OR.push({
assistants: { hasSome: assistants }
})
}
return filters
}
}

Issue testing RTK Query: The preloadedState argument passed to createStore has unexpected type of "array"

I'm learning RTK Query.
So, in a test, I have the following error:
The preloadedState argument passed to createStore has unexpected type of "array". Expected argument to be an object with the following keys: "queries", "mutations", "provided", "subscriptions", "config"
This is my test:
test("Can use preloadedState", () => {
const initialPosts = [
{
id: 1,
body: "Lorem ipsum",
},
];
// wrap component with custom render function
renderWithProviders(<GenericList />, {
preloadedState: {
postsSlice: initialPosts,
},
});
const loremIpsum = screen.getByText(/lorem ipsum/i);
expect(loremIpsum).toBeInTheDocument();
});
I have followed this tutorial https://redux.js.org/usage/writing-tests#preparing-initial-test-state.
This is my test-utils.js file:
import React from 'react'
import { render } from '#testing-library/react'
import { Provider } from 'react-redux'
import { setupStore } from '../../store/index'
import { setupListeners } from '#reduxjs/toolkit/dist/query'
export function renderWithProviders(
ui,
{
preloadedState = {},
// Automatically create a store instance if no store was passed in
store = setupStore(preloadedState),
...renderOptions
} = {}
) {
setupListeners(store.dispatch);
function Wrapper({ children }) {
return <Provider store={store}>{children}</Provider>
}
return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) }
}
This is my store:
import { configureStore } from "#reduxjs/toolkit";
import { postsSlice } from "../features/postsSlice";
export const setupStore = preloadedState => {
return configureStore({
reducer: {
[postsSlice.reducerPath]: postsSlice.reducer,
},
preloadedState,
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
immutableCheck: false,
serializableCheck: false,
}).concat(postsSlice.middleware),
})
}
And finally this is the postsSlice
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
export const postsSlice = createApi({
// Reducer Path it's name shown on Redux Tab
reducerPath: "postsSlice",
baseQuery: fetchBaseQuery({
baseUrl: process.env.REACT_APP_BACKEND_URL,
}),
// With tag type we can invalidate cache
tagTypes: ['posts'],
endpoints: (builder) => ({
getPosts: builder.query({
query: () => "/posts"
})
})
});
export const { useGetPostsQuery } = postsSlice;
You cannot just make up some random contents for your initialState, it has to be exactly the structure of your Redux state. And for RTK Query, that is a very complex internal structure that you should probably not mock (it could change in another version!).
Honestly, to your last question, this is a step backwards - if you want to test RTK Query, test it with a normal Redux store and mock the api.
All you were missing was to wait in your test until the result was rendered.
Faking internal data structures means that your test will just test a very small part of what actually happens.

ngrx jasmine-marbles test resulting the "Received" part returning '?' question mark

I am having an issue with the following tech-stack:
Angular v8,
ionic Angular v5,
ngrx v8,
jasmine-marbles v0.8.3.
I am writing a unit test for "ngrx", in particular the "effects" part.
Following is my code snippet:
import { TestBed } from '#angular/core/testing';
import { provideMockActions } from '#ngrx/effects/testing';
import { Observable } from 'rxjs';
import { InformationEffects } from './information.effects';
import { HttpClientTestingModule, HttpTestingController } from '#angular/common/http/testing';
import { Storage } from '#ionic/storage';
import { DataService } from 'src/app/shared/services/data.service';
import { RouterTestingModule } from '#angular/router/testing';
import { cold, hot } from 'jasmine-marbles';
import { MockStore, provideMockStore } from '#ngrx/store/testing';
import {
InformationRequested,
InformationSuccess,
} from './information.actions';
describe('Information Effects', () => {
let information = {} as any;
const initialState = { information: information};
let actions$: Observable<any>;
let effects: InformationEffects;
let store: MockStore<any>;
let dataService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, RouterTestingModule],
providers: [
{
provide: Storage,
},
InformationEffects,
MockStore,
provideMockStore({ initialState }),
provideMockActions(() => actions$),
{
provide: DataService,
useValue: jasmine.createSpyObj('DataService', ['getInformation'])
}
]
});
effects = TestBed.get(InformationEffects);
store = TestBed.get(MockStore);
dataService = TestBed.get(DataService);
});
it('should be created', () => {
expect(effects).toBeTruthy();
});
describe('INFORMATION_REQUESTED', () => {
it('should return an InformationSucess action, with the user, on success', () => {
let language = {} as any;
const action = new InformationRequested(language);
const outcome = new InformationSuccess(information);
actions$ = hot('-a-', { a: action });
const response = cold('-a|', { a: information });
const expected = cold('--b', { b: outcome });
dataService.getInformation.and.returnValue(response);
expect(effects.informationAction$).toBeObservable(expected);
});
});
});
When I run the test using "npm test", it failed at the "Received" part showing "?".
It says:
Expected: --b,
Received: --?,
Expected:
[{"frame":20,"notification":{"kind":"N","value":{"payload":{},"type":"[Information]
INFORMATION Success"},"hasValue":true}}]
Received:
[{"frame":20,"notification":{"kind":"N","value":{"payload":{},"type":"[Information]
INFORMATION Success"},"hasValue":true}}],
Please refer below figure:
enter image description here
I had searched the internet for this question mark in the "Received:" section, to no avail. I had also researched on each jasmine-marbles syntaxes, as well the (hot & cold) observable, to understand why & how to use it. Still no solution to get rid of the '?' question mark to have the unit-test being "Success". As such please help me.

How to use node-simple-schema reactively?

Given that there is not much examples about this, I am following the docs as best as I can, but the validation is not reactive.
I declare a schema :
import { Tracker } from 'meteor/tracker';
import SimpleSchema from 'simpl-schema';
export const modelSchema = new SimpleSchema({
foo: {
type: String,
custom() {
setTimeout(() => {
this.addValidationErrors([{ name: 'foo', type: 'notUnique' }]);
}, 100); // simulate async
return false;
}
}
}, {
tracker: Tracker
});
then I use this schema in my component :
export default class InventoryItemForm extends TrackerReact(Component) {
constructor(props) {
super(props);
this.validation = modelSchema.newContext();
this.state = {
isValid: this.validation.isValid()
};
}
...
render() {
...
const errors = this.validation._validationErrors;
return (
...
)
}
}
So, whenever I try to validate foo, the asynchronous' custom function is called, and the proper addValidationErrors function is called, but the component is never re-rendered when this.validation.isValid() is supposed to be false.
What am I missing?
There are actually two errors in your code. Firstly this.addValidationErrors cannot be used asynchronously inside custom validation, as it does not refer to the correct validation context. Secondly, TrackerReact only registers reactive data sources (such as .isValid) inside the render function, so it's not sufficient to only access _validationErrors in it. Thus to get it working you need to use a named validation context, and call isValid in the render function (or some other function called by it) like this:
in the validation
custom() {
setTimeout(() => {
modelSchema.namedContext().addValidationErrors([
{ name: 'foo', type: 'notUnique' }
]);
}, 100);
}
the component
export default class InventoryItemForm extends TrackerReact(Component) {
constructor(props) {
super(props);
this.validation = modelSchema.namedContext();
}
render() {
let errors = [];
if (!this.validation.isValid()) {
errors = this.validation._validationErrors;
}
return (
...
)
}
}
See more about asynchronous validation here.

React/Meteor Component not passing Props properly

new to Meteor and running into this issue. I am using Meteor 1.3.3
When I try to pass props from my parent Container to my React Component it keeps throwing an error I will post below.
Here is my React component Prospect.jsx:
import React from 'react'
import { createContainer } from 'meteor/react-meteor-data'
import { Residents } from '/collections/residents.jsx'
import ReactDOM from 'react-dom';
import RaisedButton from 'material-ui/RaisedButton';
// import '/collections/residents.jsx'
class Prospect extends React.Component {
render() {
return(
<div>
<h1>Prospect Resident - {this.props.prospect.name.first} </h1>
<RaisedButton label="Default" />
</div>
)
}
}
Prospect.propTypes = {
// prospect: React.PropTypes.object
}
export default createContainer((params) => {
const paramsId = params.params.prospectId
Meteor.subscribe('residents');
// Meteor.subscribe('resident');
prospect = Residents.find({_id: paramsId}).fetch()
console.log(prospect[0])
return {
prospect: prospect
}
}, Prospect)
and here is my Mongo collection
residents.jsx
import { Mongo } from 'meteor/mongo'
export const Residents = new Mongo.Collection('residents')
const nameSchema = new SimpleSchema({
first: {type: String},
last: {type: String}
})
const residentSchema = new SimpleSchema({
cId: { type: String },
name: { type: nameSchema },
status: { type: String },
})
Residents.attachSchema(residentSchema)
// METHODS
Meteor.methods({
'residents.insert'(resident) {
Residents.insert(resident)
}
})
// PUBLICATIONS
if(Meteor.isServer) {
Meteor.publish('residents', function() {
return Residents.find()
})
Meteor.publish('resident', function(id) {
return Residents.find({_id: id})
})
}
and here is my Route
FlowRouter.route('/prospects/:prospectId}', {
name: 'prospectShow',
action(params) {
mount(LoggedIn, { content:
<MuiThemeProvider muiTheme={getMuiTheme()}>
<Prospect params={{prospectId: params.prospectId}} />
</MuiThemeProvider>
})
}
So when I go to localhost:3000 route I get the error
Prospect.jsx:14Uncaught TypeError: Cannot read property 'name' of undefined
Exception from Tracker recompute function:
debug.js:41 TypeError: Cannot read property '_currentElement' of null
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:772)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:753)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:672)
at ReactCompositeComponentWrapper.receiveComponent (ReactCompositeComponent.js:571)
at Object.receiveComponent (ReactReconciler.js:127)
at ReactCompositeComponentWrapper._updateRenderedComponent (ReactCompositeComponent.js:775)
at ReactCompositeComponentWrapper._performComponentUpdate (ReactCompositeComponent.js:753)
at ReactCompositeComponentWrapper.updateComponent (ReactCompositeComponent.js:672)
at ReactCompositeComponentWrapper.performUpdateIfNecessary (ReactCompositeComponent.js:585)
at Object.performUpdateIfNecessary (ReactReconciler.js:160)
My console.log(prospect[0]) in the container returns the object just fine, and it also works if I pass it in like this
return {
prospect: {name: {first: 'Joe', last: 'Smith'}}
}
So it's something about the returned object I think. Any help would be greatly appreciated, thanks
I ended up going with a solution like this. If anyone wants to answer and explain why this is needed (I thought in meteor 1.3 this wasn't needed anymore) I will accept your answer.
import React from 'react'
import { createContainer } from 'meteor/react-meteor-data'
import { Residents } from '/collections/residents.jsx'
class Prospect extends React.Component {
render() {
if(!this.props.ready){return <span>Loading...</span>}
const { prospect } = this.props
return(
<div>
<h1>{prospect.name.first} {prospect.name.last}</h1>
<div>Company: <b>{prospect.cId}</b></div>
</div>
)
}
}
Prospect.propTypes = {
ready: React.PropTypes.bool.isRequired,
prospect: React.PropTypes.object.isRequired
}
export default createContainer((params) => {
return {
ready: Meteor.subscribe('resident', params.id).ready(),
prospect: Residents.findOne(params.id)
}
}, Prospect)