Import file from packages folder structure in Meteor - meteor

I can't seem to get an import statement to work within my packages folder.
I have a file structure like the following:
-client
-model
-users.js
-packages
-mobile
-browser
-client
-auth
-login
-login.component.js
in login.component.js I need to import { user } from '/model/users.js'. Is this possible? If so how do I do it? If not what is a good workaround?
Edit: users.js
const User = Class.create({
name: 'User',
collection: Meteor.users,
secured: true,
fields: {
username: {type: String },
createdAt: { type: Date },
userData: { type: UserData, optional:true },
fullName: {
type: String,
resolve(doc) {
if (doc && doc.userData)
return doc.userData.firstName + ' ' + doc.userData.lastName;
else {
"no name"
}
}
}
}
});
export { User };

import statements are necessary for files in the /imports folder. Everything outside of that folder will be auto-loaded for you, so you don't need to import it manually.
More detail here: https://guide.meteor.com/structure.html#intro-to-import-export
Just make sure your User object is global, i.e., change
const User = Class.create({
to
User = Class.create({

Related

How to configure cypress-sql-server with no cypress.json? (updated)

I'm trying to setup cypress-sql-server, but I'm using version 10.8.0, which does not use cypress.json to configure the environment. All of the setup instructions I've found refer to using cypress.json to configure the plug-in. With the help of u/Fody, I'm closer, but I'm still running into an error:
tasksqlServer:execute, SELECT 'Bob'
CypressError
cy.task('sqlServer:execute') failed with the following error:
The 'task' event has not been registered in the setupNodeEvents method. You must register it before using cy.task()
Fix this in your setupNodeEvents method here:
D:\git\mcare.automation\client\cypress\cypress.config.jsLearn more
node_modules/cypress-sql-server/src/commands/db.js:7:1
5 | }
6 |
> 7 | cy.task('sqlServer:execute', query).then(response => {
| ^
8 | let result = [];
9 |
cypress.config.js
const { defineConfig } = require("cypress");
const sqlServer = require("cypress-sql-server");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// allows db data to be accessed in tests
config.db = {
"userName": "user",
"password": "pass",
"server": "myserver",
"options": {
"database": "mydb",
"encrypt": true,
"rowCollectionOnRequestCompletion": true
}
}
// code from /plugins/index.js
const tasks = sqlServer.loadDBPlugin(config.db);
on('task', tasks);
return config
// implement node event listeners here
},
},
});
testSQL.spec.js
describe('Testing SQL queries', () => {
it("It should return Bob", () => {
cy.sqlServer("SELECT 'Bob'").should('eq', 'Bob');
});
})
My versions:
\cypress> npx cypress --version
Cypress package version: 10.8.0
Cypress binary version: 10.8.0
Electron version: 19.0.8
Bundled Node version:
16.14.2
Suggestions? Is there any more info I can provide to help?
This is the install instruction currently given by cypress-sql-server for Cypress v9
Plugin file
The plug-in can be initialised in your cypress/plugins/index.js file as below.
const sqlServer = require('cypress-sql-server');
module.exports = (on, config) => {
tasks = sqlServer.loadDBPlugin(config.db);
on('task', tasks);
}
Translating that into Cypress v10+
const { defineConfig } = require('cypress')
const sqlServer = require('cypress-sql-server');
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// allows db data to be accessed in tests
config.db = {
"userName": "user",
"password": "pass",
"server": "myserver",
"options": {
"database": "mydb",
"encrypt": true,
"rowCollectionOnRequestCompletion": true
}
}
// code from /plugins/index.js
const tasks = sqlServer.loadDBPlugin(config.db);
on('task', tasks);
return config
},
},
})
Other variations work, such as putting the "db": {...} section below the "e2e: {...}" section, but not in the "env": {...} section.
Custom commands
Instructions for Cypress v9
Commands file
The extension provides multiple sets of commands. You can import the ones you need.
Example support/index.js file.
import sqlServer from 'cypress-sql-server';
sqlServer.loadDBCommands();
For Cypress v10+
Just move this code to support/e2e.js
cypress.json is a way to specify Cypress environment variables. Instead of using a cypress.json file, you can use any of the strategies in that link.
If you just wanted to include them in your cypress.config.js, it would look something like this:
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:1234',
env: {
db: {
// your db values here
}
}
}
})

How to connect google analytics to Nuxt3 app?

I have a problem. I try to connect my Nuxt3 app with Google Analytics.
right now I do it by adding to nuxt.config.ts following code
export default defineNuxtConfig({
buildModules: [
'#nuxtjs/google-analytics'
],
googleAnalytics: {
id: process.env.GOOGLE_ANALYTICS_ID
},
})
but unfortunately I get following error when I try to build my app
ERROR Error compiling template: { 17:53:04
ssr: false,
src: 'C:\\Users\\szczu\\Elektryk\\node_modules\\#nuxtjs\\google-analytics\\lib\\plugin.js',
fileName: 'google-analytics.js',
options: {
dev: true,
debug: {
sendHitTask: true
},
id: undefined
},
filename: 'google-analytics.js',
dst: 'C:/Users/szczu/Elektryk/.nuxt/google-analytics.js'
}
ERROR serialize is not defined 17:53:04
at eval (eval at <anonymous> (node_modules\lodash.template\index.js:1550:12), <anonymous>:7:1)
at compileTemplate (/C:/Users/szczu/Elektryk/node_modules/#nuxt/kit/dist/index.mjs:493:45)
at async /C:/Users/szczu/Elektryk/node_modules/nuxt3/dist/chunks/index.mjs:1296:22
at async Promise.all (index 11)
at async generateApp (/C:/Users/szczu/Elektryk/node_modules/nuxt3/dist/chunks/index.mjs:1295:3)
at async _applyPromised (/C:/Users/szczu/Elektryk/node_modules/perfect-debounce/dist/index.mjs:54:10)
Does anyone have an idea how to fix it?
Try the vue-vtag-next package as a plugin
yarn add --dev vue-gtag-next
Create a plugin file plugins/vue-gtag.client.js
import VueGtag from 'vue-gtag-next'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueGtag, {
property: {
id: 'GA_MEASUREMENT_ID'
}
})
})
Late reply, but i would like to add for any future viewers.
The above solution only worked for me when the $router was passed. Please find below sample code.
Please also note:
The package being used, 'vue-gtag' instead of 'vue-gtag-next'.
You have to pass config object instead of property for the 'vue-gtag' package
import VueGtag from 'vue-gtag'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueGtag, {
config: {
id: 'GA_MEASUREMENT_ID',
},
}, nuxtApp.$router)
})
found this solution https://github.com/nuxt/framework/discussions/5702
.. And also you may use nuxt.config to provide app.head.script with children attribute on the app level:
import { defineNuxtConfig } from "nuxt";
export default defineNuxtConfig({
app: {
head: {
script: [{ children: 'console.log("test3");' }],
},
},
});
import VueGtag from 'vue-gtag-next'
export default defineNuxtPlugin(async (nuxtApp) => {
const { data: { value: {google_id, google_sv, yandex_id, privacy_policy} } } = await useMyApi("/api/main/site-metriks/");
nuxtApp.vueApp.use(VueGtag, {
property: {
id: google_id
}
})
})
For Nuxt 3:
Install vue-gtm: npm i #gtm-support/vue-gtm
Create file in /plugins/vue-gtm.client.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(createGtm({
id: 'GTM-ID',
defer: false, // Script can be set to `defer` to speed up page load at the cost of less accurate results (in case visitor leaves before script is loaded, which is unlikely but possible). Defaults to false, so the script is loaded `async` by default
compatibility: false, // Will add `async` and `defer` to the script tag to not block requests for old browsers that do not support `async`
nonce: '2726c7f26c', // Will add `nonce` to the script tag
enabled: true, // defaults to true. Plugin can be disabled by setting this to false for Ex: enabled: !!GDPR_Cookie (optional)
debug: true, // Whether or not display console logs debugs (optional)
loadScript: true, // Whether or not to load the GTM Script (Helpful if you are including GTM manually, but need the dataLayer functionality in your components) (optional)
vueRouter: useRouter(), // Pass the router instance to automatically sync with router (optional)
//ignoredViews: ['homepage'], // Don't trigger events for specified router names (optional)
trackOnNextTick: false, // Whether or not call trackView in Vue.nextTick
}))
})
Nuxt would automatically pick up this plugin and you're done.

How do you get the currently active notebook name in JupyterLab?

I'm working on creating a server-side extension in JupyterLab and have been searching for quite a while for a way to get the currently active notebook name inside my index.ts file. I found this existing extension that gets the currently active tab name with ILabShell and sets the browser tab to have the same name. In my case I'll be triggering the process with a button on the notebook toolbar so the active tab will always be a notebook. However, when I try to use the code in the activate section of my extension, I get TypeError: labShell.currentChanged is undefined where labShell is an instance of ILabShell. That extension doesn't support JupyterLab 3.0+ so I believe that's part of the problem. However, currentChanged for ILabShell is clearly defined here. What if anything can I change to make it work? Is there another way to accomplish what I'm trying to do? I'm aware of things like this to get the notebook name inside the notebook but that's not quite what I'm trying to do.
Windows 10,
Node v14.17.0,
npm 6.14.13,
jlpm 1.21.1,
jupyter lab 3.0.14
I'm using this example server extension as a template: https://github.com/jupyterlab/extension-examples/tree/master/server-extension
index.ts file from the existing extension to get the current tab name:
import {
ILabShell,
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '#jupyterlab/application';
import { Title, Widget } from '#lumino/widgets';
/**
* Initialization data for the jupyterlab-active-as-tab-name extension.
*/
const extension: JupyterFrontEndPlugin<void> = {
id: 'jupyterlab-active-as-tab-name',
autoStart: true,
requires: [ILabShell],
activate: (app: JupyterFrontEnd, labShell: ILabShell) => {
const onTitleChanged = (title: Title<Widget>) => {
console.log('the JupyterLab main application:', title);
document.title = title.label;
};
// Keep the session object on the status item up-to-date.
labShell.currentChanged.connect((_, change) => {
const { oldValue, newValue } = change;
// Clean up after the old value if it exists,
// listen for changes to the title of the activity
if (oldValue) {
oldValue.title.changed.disconnect(onTitleChanged);
}
if (newValue) {
newValue.title.changed.connect(onTitleChanged);
}
});
}
};
export default extension;
My index.ts file:
import {
ILabShell,
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '#jupyterlab/application';
import { ICommandPalette } from '#jupyterlab/apputils';
import { ILauncher } from '#jupyterlab/launcher';
import { requestAPI } from './handler';
import { ToolbarButton } from '#jupyterlab/apputils';
import { DocumentRegistry } from '#jupyterlab/docregistry';
import { INotebookModel, NotebookPanel } from '#jupyterlab/notebook';
import { IDisposable } from '#lumino/disposable';
import { Title, Widget } from '#lumino/widgets';
export class ButtonExtension implements DocumentRegistry.IWidgetExtension<NotebookPanel, INotebookModel> {
constructor(app: JupyterFrontEnd) {
this.app = app;
}
readonly app: JupyterFrontEnd
createNew(panel: NotebookPanel, context: DocumentRegistry.IContext<INotebookModel>): IDisposable {
// dummy json data to test post requests
const data2 = {"test message" : "message"}
const options = {
method: 'POST',
body: JSON.stringify(data2),
headers: {'Content-Type': 'application/json'
}
};
// Create the toolbar button
let mybutton = new ToolbarButton({
label: 'Measure Energy Usage',
onClick: async () => {
// POST request to Jupyter server
const dataToSend = { file: 'nbtest.ipynb' };
try {
const reply = await requestAPI<any>('hello', {
body: JSON.stringify(dataToSend),
method: 'POST'
});
console.log(reply);
} catch (reason) {
console.error(
`Error on POST /jlab-ext-example/hello ${dataToSend}.\n${reason}`
);
}
// sample POST request to svr.js
fetch('http://localhost:9898/api', options);
}
});
// Add the toolbar button to the notebook toolbar
panel.toolbar.insertItem(10, 'MeasureEnergyUsage', mybutton);
console.log("MeasEnerUsage activated");
// The ToolbarButton class implements `IDisposable`, so the
// button *is* the extension for the purposes of this method.
return mybutton;
}
}
/**
* Initialization data for the server-extension-example extension.
*/
const extension: JupyterFrontEndPlugin<void> = {
id: 'server-extension-example',
autoStart: true,
optional: [ILauncher],
requires: [ICommandPalette, ILabShell],
activate: async (
app: JupyterFrontEnd,
palette: ICommandPalette,
launcher: ILauncher | null,
labShell: ILabShell
) => {
console.log('JupyterLab extension server-extension-example is activated!');
const your_button = new ButtonExtension(app);
app.docRegistry.addWidgetExtension('Notebook', your_button);
// sample GET request to jupyter server
try {
const data = await requestAPI<any>('hello');
console.log(data);
} catch (reason) {
console.error(`Error on GET /jlab-ext-example/hello.\n${reason}`);
}
// get name of active tab
const onTitleChanged = (title: Title<Widget>) => {
console.log('the JupyterLab main application:', title);
document.title = title.label;
};
// Keep the session object on the status item up-to-date.
labShell.currentChanged.connect((_, change) => {
const { oldValue, newValue } = change;
// Clean up after the old value if it exists,
// listen for changes to the title of the activity
if (oldValue) {
oldValue.title.changed.disconnect(onTitleChanged);
}
if (newValue) {
newValue.title.changed.connect(onTitleChanged);
}
});
}
};
export default extension;
There are two ways to fix it and one way to improve it. I recommend using (2) and (3).
Your order of arguments in activate is wrong. There is no magic matching of argument types to signature function; instead arguments are passed in the order given in requires and then optional. This means that you will receive:
...[JupyterFrontEnd, ICommandPalette, ILabShell, ILauncher]
but what you are expecting is:
...[JupyterFrontEnd, ICommandPalette, ILauncher, ILabShell]
In other words, optionals are always at the end. There is no static type check so this is a common source of mistakes - just make sure you double check the order next time (or debug/console.log to see what you are getting).
Actually, don't require ILabShell as a token. Use the ILabShell that comes in app.shell instead. This way your extension will be also compatible with other frontends built using JupyterLab components.
shell = app.shell as ILabShell
(optional improvement) install RetroLab as a development-only requirement and use import type (this way it is not a runtime requirement) to ensure compatibility with RetroLab:
import type { IRetroShell } from '#retrolab/application';
// ... and then in `activate()`:
shell = app.shell as ILabShell | IRetroShell
to be clear: not doing so would not make your extension incompatible; what it does is ensures you do not make it incompatible by depending on lab-specific behaviour of the ILabShell in the future.
So in total it would look like:
import {
ILabShell,
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '#jupyterlab/application';
import type { IRetroShell } from '#retrolab/application';
// ...
const extension: JupyterFrontEndPlugin<void> = {
id: 'server-extension-example',
autoStart: true,
optional: [ILauncher],
requires: [ICommandPalette],
activate: async (
app: JupyterFrontEnd,
palette: ICommandPalette,
launcher: ILauncher | null
) => {
let shell = app.shell as ILabShell | IRetroShell ;
shell.currentChanged.connect((_, change) => {
console.log(change);
// ...
});
}
};
export default extension;

Is there a way to show related model ids without sideloading or embedding data

My understanding is that using serializeIds: 'always' will give me this data, but it does not.
Here's what I'm expecting:
{
id="1"
title="some title"
customerId="2"
}
Instead the output I'm receiving is:
{
id="1"
title="some title"
}
My code looks something like this:
import {
Server,
Serializer,
Model,
belongsTo,
hasMany,
Factory
} from "miragejs";
import faker from "faker";
const ApplicationSerializer = Serializer.extend({
// don't want a root prop
root: false,
// true required to have root:false
embed: true,
// will always serialize the ids of all relationships for the model or collection in the response
serializeIds: "always"
});
export function makeServer() {
let server = newServer({
models: {
invoice: Model.extend({
customer: belongsTo()
}),
customer: Model.extend({
invoices: hasMany()
})
},
factories: {
invoice: Factory.extend({
title(i) {
return `Invoice ${i}`;
},
afterCreate(invoice, server) {
if (!invoice.customer) {
invoice.update({
customer: server.create("customer")
});
}
}
}),
customer: Factory.extend({
name() {
let fullName = () =>
`${faker.name.firstName()} ${faker.name.lastName()}`;
return fullName;
}
})
},
seeds(server) {
server.createList("invoice", 10);
},
serializers: {
application: ApplicationSerializer,
invoice: ApplicationSerializer.extend({
include: ["customer"]
})
},
routes() {
this.namespace = "api";
this.get("/auth");
}
});
}
Changing the config to root: true, embed: false, provides the correct output in the invoice models, but adds the root and sideloads the customer, which I don't want.
You've run into some strange behavior with how how serializeIds interacts with embed.
First, it's confusing why you need to set embed: true when you're just trying to disable the root. The reason is because embed defaults to false, so if you remove the root and try to include related resources, Mirage doesn't know where to put them. This is a confusing mix of options and Mirage should really have different "modes" that take this into account.
Second, it seems that when embed is true, Mirage basically ignores the serializeIds option, since it thinks your resources will always be embedded. (The idea here is that a foreign key is used to fetch related resources separately, but when they're embedded they always come over together.) This is also confusing and doesn't need to be the case. I've opened a tracking issue in Mirage to help address these points.
As for you today, the best way to solve this is to leave root to true and embed false, which are both the defaults, so that serializeIds works properly, and then just write your own serialize() function to remove the key for you:
const ApplicationSerializer = Serializer.extend({
// will always serialize the ids of all relationships for the model or collection in the response
serializeIds: "always",
serialize(resource, request) {
let json = Serializer.prototype.serialize.apply(this, arguments);
let root = resource.models ? this.keyForCollection(resource.modelName) : this.keyForModel(resource.modelName)
return json[root];
}
});
You should be able to test this out on both /invoices and /invoices/1.
Check out this REPL example and try making a request to each URL.
Here's the config from the example:
import {
Server,
Serializer,
Model,
belongsTo,
hasMany,
Factory,
} from "miragejs";
import faker from "faker";
const ApplicationSerializer = Serializer.extend({
// will always serialize the ids of all relationships for the model or collection in the response
serializeIds: "always",
serialize(resource, request) {
let json = Serializer.prototype.serialize.apply(this, arguments);
let root = resource.models ? this.keyForCollection(resource.modelName) : this.keyForModel(resource.modelName)
return json[root];
}
});
export default new Server({
models: {
invoice: Model.extend({
customer: belongsTo(),
}),
customer: Model.extend({
invoices: hasMany(),
}),
},
factories: {
invoice: Factory.extend({
title(i) {
return "Invoice " + i;
},
afterCreate(invoice, server) {
if (!invoice.customer) {
invoice.update({
customer: server.create("customer"),
});
}
},
}),
customer: Factory.extend({
name() {
return faker.name.firstName() + " " + faker.name.lastName();
},
}),
},
seeds(server) {
server.createList("invoice", 10);
},
serializers: {
application: ApplicationSerializer,
},
routes() {
this.resource("invoice");
},
});
Hopefully that clears things up + sorry for the confusing APIs!

Collection2 relationship error due to missing collection object

I am attempting to create a relationship between 2 collections, but one of the collections is not available to be referenced in the other. Specifically, I have 2 collections: Sites and ContentTypes. This is what they include:
// app/lib/collections/sites.js
Sites = new Mongo.Collection('sites');
Sites.attachSchema(new SimpleSchema({
name: {
type: String,
label: "Name",
max: 100
},
client: {
type: String,
label: "Client",
max: 100
},
created: {
type: Date,
autoValue: function() {
if (this.isInsert) {
return new Date;
} else if (this.isUpsert) {
return {$setOnInsert: new Date};
} else {
this.unset(); // Prevent user from supplying their own value
}
}
}
}));
And here's the ContentTypes collection:
// app/lib/collections/content_types.js
ContentTypes = new Mongo.Collection('content_types');
ContentTypes.attachSchema(new SimpleSchema({
name: {
type: String,
label: "Name",
max: 100
},
machineName: {
type: String,
label: "Machine Name",
max: 100
},
site:{
type: Sites
},
created: {
type: Date,
autoValue: function() {
if (this.isInsert) {
return new Date;
} else if (this.isUpsert) {
return {$setOnInsert: new Date};
} else {
this.unset(); // Prevent user from supplying their own value
}
}
}
}));
When I add the Sites reference to the ContentTypes schema, I the app crashes with the error:
ReferenceError: Sites is not defined
at lib/collections/content_types.js:32:11
I haven't had much luck finding documentation for relationships in collection2 beyond this. It looks like the format referenced there is supposed to work based on this thread.
This is due to the order Meteor loads files. See section on File Load Order here:
There are several load ordering rules. They are applied sequentially to all applicable files in the application, in the priority given below:
HTML template files are always loaded before everything else
Files beginning with main. are loaded last
Files inside any lib/ directory are loaded next
Files with deeper paths are loaded next
Files are then loaded in alphabetical order of the entire path
e.g. rename app/lib/collections/sites.js to app/lib/collections/a_sites.js and the Sites variable will be defined when the content_types.js file is being loaded.

Resources