How to Declare Typegoose Models instead of instantiate - typegoose

According to the docs - you need to 1) declare a class, 2) get the model for class and 3) use the model to create or retrieve data into another object ( see below )
It seems a waste of overhead to create/declare the model each time by way of a function ( getModelForClass ) . Is it possible to declare this by way of inheriting or implementing the properties needed to create this in a module?
class User {
#prop()
public name?: string;
}
const UserModel = getModelForClass(User); // UserModel is a regular Mongoose Model with correct types
(async () => {
await mongoose.connect('mongodb://localhost:27017/', { useNewUrlParser: true, useUnifiedTopology: true, dbName: "test" });
const { _id: id } = await UserModel.create({ name: 'JohnDoe' } as User);
// an "as" assertion, to have types for all properties
const user = await UserModel.findById(id).exec();

This is not possible without typegoose fully reimplementing the way how schemas & models are made in mongoose

Related

Nuxt: How to access store in rollbar.js WITHOUT using localStorage?

I am using Nuxt and Rollbar. I have a user id state in store.
My question is, how can I set this user id as a custom payload in the transformer function in rollbar.js WITHOUT using localStorage?
Here is my code:
// plugins/rollbar.js
const transformer = function(payload) {
payload.user_id = user_id_from_store // how to get this from store?
}
// store/index.js
export const state = () => ({
userId: ''
})
export const mutations = {
setUserId(state, userId) {
state.userId = userId
}
}
//components/MyComponent.vue
methods: {
fetch() {
const userId = fetchUserId()
this.$store.commit('setUserId', userId)
}
}
Things I have tried:
In rollbar.js, create and export a function which takes a context object as argument. Then call this function in transformer function to get user_id:
// plugins/rollbar.js
const getUserId = context => {
const user_id = context.store.state.userId
return user_id
}
const transformer = function(payload) {
payload.user_id = getUserId()
}
export default getUserId
When I console.log(context.store)in getUserId function, I got a Store object, but calling the function in transformer function threw Rollbar: Error while calling custom transform() function. Removing custom transform(). TypeError: Cannot read property 'store' of undefined.
At the end, OP succeeded thanks to inject, more info available here: https://nuxtjs.org/docs/2.x/directory-structure/plugins#inject-in-root--context
This one is indeed needed for libraries that are not directly into the Vue ecosystem but that we wish to have working in our Nuxt app.

Vuex-module-decorator, modifying state inside an action

Using the vuex-module-decorator I have a authenticate action that should mutate the state.
#Action
public authenticate(email: string, password: string): Promise<Principal> {
this.principal = null;
return authenticator
.authenticate(email, password)
.then(auth => {
const principal = new Principal(auth.username);
this.context.commit('setPrincipal', principal);
return principal;
})
.catch(error => {
this.context.commit('setError', error);
return error;
});
}
// mutations for error and principal
But this fail with the following message:
Unhandled promise rejection Error: "ERR_ACTION_ACCESS_UNDEFINED: Are you trying to access this.someMutation() or this.someGetter inside an #Action?
That works only in dynamic modules.
If not dynamic use this.context.commit("mutationName", payload) and this.context.getters["getterName"]
What I don't understand is that it works well with #MutationAction and async. However I miss the return type Promise<Principal>.
#MutationAction
public async authenticate(email: string, password: string) {
this.principal = null;
try {
const auth = await authenticator.authenticate(email, password);
return { principal: new Principal(auth.username), error: null };
} catch (ex) {
const error = ex as Error;
return { principal: null, error };
}
}
--
At this time I feel blocked and would like to have some help to implement an #Action that can mutate the state and return a specific type in a Promise.
Just add rawError option to the annotation so it becomes
#Action({rawError: true})
And it display error normally. this is because the the library "vuex-module-decorators" wrap error so by doing this you will able to get a RawError that you can work with
You can vote down this answer if you would like because it isn't answering the specific question being posed. Instead, I am going to suggest that if you are using typescript, then don't use vuex. I have spent the past month trying to learn vue /vuex and typescript. The one thing I am committed to is using typescript because I am a firm believer in the benefits of using typescript. I will never use raw javascript again.
If somebody would have told me to not use vuex from the beginning, I would have saved myself 3 of the past 4 weeks. So I am here to try and share that insight with others.
The key is Vue 3's new ref implementation. It is what really changes the game for vuex and typescript. It allows us to not have to rely on vuex to automatically wrap state in a reactive. Instead, we can do that ourselves with the ref construct in vue 3. Here is a small example from my app that uses ref and a typescript class where I was expecting to use vuex in the past.
NOTE1: the one thing you lose when using this approach is vuex dev tools.
NOTE2: I might be biased as I am ported 25,000 lines of typescript (with 7000 unit tests) from Knockout.js to Vue. Knockout.js was all about providing Observables (Vue's ref) and binding. Looking back, it was kind of ahead of its time, but it didn't get the following and support.
Ok, lets create a vuex module class that doesn't use vuex. Put this in appStore.ts. To simplify it will just include the user info and the id of the club the user is logged into. A user can switch clubs so there is an action to do that.
export class AppClass {
public loaded: Ref<boolean>;
public userId: Ref<number>;
public userFirstName: Ref<string>;
public userLastName: Ref<string>;
// Getters are computed if you want to use them in components
public userName: Ref<string>;
constructor() {
this.loaded = ref(false);
initializeFromServer()
.then(info: SomeTypeWithSettingsFromServer) => {
this.userId = ref(info.userId);
this.userFirstName = ref(info.userFirstName);
this.userLastName = ref(info.userLastName);
this.userName = computed<string>(() =>
return this.userFirstName.value + ' ' + this.userLastName.value;
}
}
.catch(/* do some error handling here */);
}
private initializeFromServer(): Promise<SomeTypeWithSettingsFromServer> {
return axios.get('url').then((response) => response.data);
}
// This is a getter that you don't need to be reactive
public fullName(): string {
return this.userFirstName.value + ' ' + this.userLastName.value;
}
public switchToClub(clubId: number): Promise<any> {
return axios.post('switch url')
.then((data: clubInfo) => {
// do some processing here
}
.catch(// do some error handling here);
}
}
export appModule = new AppClass();
Then when you want to access appModule anywhere, you end up doing this:
import { appModule } from 'AppStore';
...
if (appModule.loaded.value) {
const userName = appModule.fullName();
}
or in a compositionApi based component. This is what would replace mapActions etc.
<script lang="ts">
import { defineComponent } from '#vue/composition-api';
import { appModule } from '#/store/appStore';
import footer from './footer/footer.vue';
export default defineComponent({
name: 'App',
components: { sfooter: footer },
props: {},
setup() {
return { ...appModule }
}
});
</script>
and now you can use userId, userFirstName, userName etc in your template.
Hope that helps.
I just added the computed getter. I need to test if that is really needed. It might not be needed because you might be able to just reference fullName() in your template and since fullName() references the .value variables of the other refs, fullName might become a reference itself. But I have to check that out first.
I sugest this simple solution, work fine for me 👌:
// In SomeClassComponent.vue
import { getModule } from "vuex-module-decorators";
import YourModule from "#/store/YourModule";
someMethod() {
const moduleStore = getModule(YourModule, this.$store);
moduleStore.someAction();
}
If the action has parameters, put them.
Taken from: https://github.com/championswimmer/vuex-module-decorators/issues/86#issuecomment-464027359

GraphQL + Relay: How can I perform authorization for refetching?

I am working on a GraphQL server built using Express and attempting to support Relay.
For a regular GraphQL query, I can handle authorization in the resolve function. E.g.:
var queryType = new GraphQLObjectType({
name: 'RootQueryType',
fields: () => ({
foo: {
type: new GraphQLList(bar),
description: 'I should have access to some but not all instances of bar',
resolve: (root, args, request) => getBarsIHaveAccessTo(request.user)
}
})
});
To support Relay refetching on the back-end, Facebook's Relay tutorial instructs us to have GraphQL objects implement a nodeInterface for mapping global ids to objects and objects to GraphQL types. The nodeInterface is defined by the nodeDefinitions function from graphql-relay.
const {nodeInterface, nodeField} = nodeDefinitions(
(globalId) => {
const {type, id} = fromGlobalId(globalId);
if (type === 'bar') {
// since I don't have access to the request object here, I can't pass the user to getBar, so getBar can't perform authorization
return getBar(id);
} else {
return null;
}
},
(obj) => {
// return the object type
}
);
The refetching function that gets passed to nodeDefinitions doesn't get passed the request object, only the global id. How can I get access to the user during refetching so I can authorize those requests?
As a sanity check, I tried querying for nodes that the authenticated user doesn't otherwise have access to (and shouldn't) through the node interface, and got the requested data back:
{node(id:"id_of_something_unauthorized"){
... on bar {
field_this_user_shouldnt_see
}
}}
=>
{
"data": {
"node": {
"field_this_user_shouldnt_see": "a secret"
}
}
}
As it turns out, the request data actually does get passed to resolve. If we look at the source, we see that nodeDefinitions tosses out the parent parameter and passes the global id, the context (containing the request data), and the info arguments from nodeField's resolve function.
Ultimately, where a resolve call would get the following arguments:
(parent, args, context, info)
the idFetcher instead gets:
(id, context, info)
So we can implement authorization as follows:
const {nodeInterface, nodeField} = nodeDefinitions(
(globalId, context) => {
const {type, id} = fromGlobalId(globalId);
if (type === 'bar') {
// get Bar with id==id if context.user has access
return getBar(context.user, id);
} else {
return null;
}
},
(obj) => {
// return the object type
}
);
https://github.com/graphql/graphql-relay-js/blob/master/src/node/node.js#L94-L102

accessing attributes in sequelize instanceMethods

I'm adding an instance method to a sequelize model. According to the documentation I should be able to reference this.name, but the only value I can see is this.dataValues.name. I have no reason to believe that the high quality documentation is wrong .... but why does this happen?
Also, there are no setters or getters available. this.getDataValue / this.setDataValue work in getters / setters, but not in instanceMethods.
I can't find any relevant samples on the net - if you know of a project that reads (or better, writes) these values, please add that to your response.
module.exports = (sequelize: Sequelize, DataTypes: DataTypes) => {
return sequelize.define<UserInstance, UserPojo>('User', {
name: { type: DataTypes.STRING }
}, {
instanceMethods: {
myMethod: (value) => {
// this.name is undefined
// this.dataValues.name IS defined ??
}
}, ...
As you can probably see, I'm using Typescript. I just examined the generated Javascript, and immediately saw the problem.
Typescript puts this at the top of the module:
var _this = this;
And references that '_this', rather than the one in the context of the function -- didn't realize that! As soon as I changed this to traditional function() { } syntax, it worked. So, if you're using typescript, you can do this:
myMethod: function(value: type) => void {
}
That is, you don't have to give up on typing your arguments and return value.

How to test a meteor method that relies on Meteor.user()

I am trying to determine the best way to test my code and am running into complications from every direction I've tried.
The basic code is this (though far more complex due to several layers of "triggers" that actually implement this):
Client populates an object
Client calls a meteor method and passes the object
Meteor method uses Meteor.user() to get the current user, adds a "createdby" attribute to the object, inserts the object and then creates another object (of a different type) with various attributes that depend on the first object, as well as a bunch of other things already in the database
I'm trying to use Velocity and Jasmine. I'd prefer to integration test these steps to create the first object and then test that the second object is properly created.
My problem is that if I do it on the server, the Meteor.user() call doesn't work. If I do it on the client, I need to subscribe to a large number of collections in order for the logic to work, which seems kludgy.
Is there a better approach? Is there a way to simulate or mock a user login in a server integration test?
In your jasmine test you can mock a call to Meteor.user() like so:
spyOn(Meteor, "user").and.callFake(function() {
return 1234; // User id
});
You may want to specify a userId or change logged state depending on executed test. I then recommend to create meteor methods in your test project:
logIn = function(userId) {
Meteor.call('logIn', userId);
};
logOut = function() {
Meteor.call('logOut');
}
Meteor.userId = function() {
return userId;
};
Meteor.user = function() {
return userId ? {
_id: userId,
username: 'testme',
emails: [{
address: 'test#domain.com'
}],
profile: {
name: 'Test'
}
} : null;
};
Meteor.methods({
logIn: function(uid) {
userId = uid || defaultUserId;
},
logOut: function() {
userId = null;
}
});
If you don't want to rely on a complete lib just for this single usecase, you can easily mock your Meteor.urser() with beforeEach and afterEach:
import {chai, assert} from 'meteor/practicalmeteor:chai';
import {Meteor} from 'meteor/meteor';
import {Random} from 'meteor/random';
describe('user mocking', () => {
let userId = null;
let userFct = null;
const isDefined = function (target) {
assert.isNotNull(target, "unexpected null value");
assert.isDefined(target, "unexpected undefined value");
if (typeof target === 'string')
assert.notEqual(target.trim(), "");
};
//------------------------------------------//
beforeEach(() => {
// save the original user fct
userFct = Meteor.user;
// Generate a real user, otherwise it is hard to test roles
userId = Accounts.createUser({username: Random.id(5)});
isDefined(userId);
// mock the Meteor.user() function, so that it
// always returns our new created user
Meteor.user = function () {
const users = Meteor.users.find({_id: userId}).fetch();
if (!users || users.length > 1)
throw new Error("Meteor.user() mock cannot find user by userId.");
return users[0];
};
});
//------------------------------------------//
afterEach(() => {
//remove the user in the db
Meteor.users.remove(userId);
// restore user Meteor.user() function
Meteor.user = userFct;
// reset userId
userId = null;
});
it("works...", () => {
// try your methods which make use of
// Meteor.user() here
});
});
It makes sure, that Meteor.user() only returns the user you created in beforeEach. This it at least a fine thing, when you want to test everything else and assume, that the user creation and Meteor.user() is working as expected (which is the essence of mocking, as I can see so far).