I have declared <all_urls> permission inside my manifest.json file and I'm using the tabs.insertCSS() to inject some css code inside a page when needed. I'm facing the error Uncaught (in promise) Error: Missing host permission for the tab in firefox extension debugger, how I can fix?
{
"manifest_version": 2,
"name": "myExtension",
"description": "",
"default_locale": "en",
"permissions": [
"<all_urls>",
"tabs",
"activeTab",
"contextMenus",
"notifications",
"webRequest",
"webRequestBlocking"
],
"icons": {
"16": "icons/16.png",
"48": "icons/48.png",
"128": "icons/128.png"
},
"background": {
"scripts": [
"js/background.js"
],
"persistent": true
}
}
background:
const checkRequest = (details) => {
console.log(details)
var blocked = ["https://www.example.com/", "https://www.example2.com/"]
var isBlocked = blocked.includes(details.url)
console.log(isBlocked)
return { cancel: isBlocked }
}
const redirectAfterBlock = (details) => {
console.log(details)
if( details.error == "NS_ERROR_ABORT" ){
browser.tabs.insertCSS({
file: browser.runtime.getURL('content-replace.css')
})
console.log("blocked")
}
}
browser.webRequest.onBeforeRequest.addListener(
checkRequest,
{
urls: ["<all_urls>"],
types: ["main_frame"]
},["blocking"])
browser.webRequest.onErrorOccurred.addListener(
redirectAfterBlock,
{
urls: ["<all_urls>"]
})
Looks like I fixed it by inserting my CSS in webRequest.onCompleted instead of onBeforeRequest:
browser.webRequest.onCompleted.addListener(
function (details) {
extensionApi.tabs
.insertCSS(
details.tabId,
{ file: "content.css" }
)
},
{ urls: ["<all_urls>"] }
);
Downside is there's a flash of unstyled page.
Related
When I go and try to run gatsby develop I get the following error:
TypeError: Cannot destructure property 'fields' of 'nodesType' as it is undefined.
at generateNodeQueriesFromIngestibleFields (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/steps/ingest-remote-schema/buil
d-queries-from-introspection/generate-queries-from-ingestable-types.js:155:13)
at buildNodeQueries (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/steps/ingest-remote-schema/build-queries-from-introspe
ction/build-node-queries.js:25:25)
at runSteps (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/utils/run-steps.ts:41:9)
at runSteps (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/utils/run-steps.ts:43:9)
at ingestRemoteSchema (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/steps/ingest-remote-schema/index.js:49:5)
at runSteps (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/utils/run-steps.ts:41:9)
at runAPI (/Users/repoFolder/Work/gatsby/node_modules/gatsby/src/utils/api-runner-node.js:487:16)
ERROR #gatsby-source-wordpress_112003
gatsby-source-wordpress
Encountered a critical error when running the buildNodeQueries build step.
See above for more information.
not finished createSchemaCustomization - 3.831s
not finished [gatsby-source-wordpress] ingest WPGraphQL schema - 2.379s
I haven't made any changes, or upgraded packages and have since been working on other projects. I tried disabling the Related Posts plugin, as well as a few others in addition to upgrading the gatsby-source-wordpress plugin.
I am configuring the plugin like in my gatsby-config.js:
{
resolve: "gatsby-source-wordpress",
options: {
url: `${process.env.WPGRAPHQL_URL}`,
verbose: false,
develop: {
hardCacheData: false,
},
schema: {
perPage: 10, // currently set to 100
requestConcurrency: 3, // currently set to 15
previewRequestConcurrency: 1, // currently set to 5
},
type: {
Page: {
exclude: true,
},
Menu: {
exclude: true,
},
MenuItem: {
exclude: true,
},
},
debug: {
graphql: {
onlyReportCriticalErrors: true,
},
},
searchAndReplace: [
{
search: `${process.env.GATSBY_WORDPRESS_URL_PROTOCOL}://${process.env.GATSBY_WORDPRESS_URL_PATH}`,
replace: `${process.env.GATSBY_SITE_URL_PROTOCOL}://${process.env.GATSBY_SITE_URL_PATH}`,
},
],
html: {
useGatsbyImage: true,
},
},
},
and I am configuring the plugin / constructing the node ingatsby-node.jslike the following:
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const BlogPostTemplate = path.resolve("./src/templates/BlogPost.tsx")
const BlogTagPostsTemplate = path.resolve(
"./src/templates/BlogTagPosts.tsx"
)
const BlogCategoryPostsTemplate = path.resolve(
"./src/templates/BlogCategoryPosts.tsx"
)
const BlogPostsResult = await graphql(`
{
allWpPost {
edges {
node {
id
slug
uri
link
title
excerpt
date(formatString: "MMMM DD, YYYY")
modified(formatString: "MMMM DD, YYYY")
author {
node {
avatar {
url
}
id
name
uri
slug
}
}
featuredImage {
node {
localFile {
childImageSharp {
gatsbyImageData(
width: 1920
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
}
categories {
nodes {
id
count
name
slug
}
}
tags {
nodes {
id
count
name
slug
}
}
}
}
}
}
`)
if (BlogPostsResult.errors) {
reporter.panicOnBuild("Error while running GraphQL query.")
return
}
const BlogPosts = BlogPostsResult.data.allWpPost.edges
BlogPosts.forEach((post, index) => {
const date = post.node.date
createPage({
path: `/${post.node.categories.nodes[0].slug}/${moment(date).format(
"YYYY"
)}/${moment(date).format("MM")}/${post.node.slug}.html`,
component: BlogPostTemplate,
context: {
id: post.node.id,
slug: post.node.slug,
uri: post.node.uri,
previous: index === 0 ? null : BlogPosts[index - 1].node,
next:
index === BlogPosts.length - 1
? null
: BlogPosts[index + 1].node,
},
})
})
createPaginatedPages({
edges: BlogPosts,
createPage: createPage,
pageTemplate: "src/templates/BlogPosts.tsx",
pageLength: 10,
pathPrefix: "blog",
})
const BlogTagPosts = new Map()
const BlogCategoryPosts = new Map()
BlogPosts.forEach((post) => {
const tags = post.node.tags.nodes
if (tags && tags.length > 0) {
tags.forEach((tag) => {
if (BlogTagPosts.has(tag.slug)) {
BlogTagPosts.set(tag.slug, [
...BlogTagPosts.get(tag.slug),
post,
])
} else {
BlogTagPosts.set(tag.slug, [post])
}
if (BlogTagPosts.has(tag.title)) {
BlogTagPosts.set(tag.title, [
...BlogTagPosts.get(tag.title),
post,
])
} else {
BlogTagPosts.set(tag.title, [post])
}
})
}
const categories = post.node.categories.nodes
if (categories && categories.length > 0) {
categories.forEach((category) => {
if (BlogCategoryPosts.has(category.slug)) {
BlogCategoryPosts.set(category.slug, [
...BlogCategoryPosts.get(category.slug),
post,
])
} else {
BlogCategoryPosts.set(category.slug, [post])
}
if (BlogCategoryPosts.has(category.title)) {
BlogCategoryPosts.set(category.title, [
...BlogCategoryPosts.get(category.title),
post,
])
} else {
BlogCategoryPosts.set(category.title, [post])
}
})
}
})
const BlogTagSlugs = [...BlogTagPosts.keys()]
const BlogCategorySlugs = [...BlogCategoryPosts.keys()]
//const BlogCategoryTitles = [...BlogCategoryPosts.keys()];
if (BlogTagSlugs.length > 0) {
BlogTagSlugs.forEach((BlogTagSlug) => {
createPage({
path: `/tag/${BlogTagSlug}`,
component: BlogTagPostsTemplate,
context: {
group: BlogTagPosts.get(BlogTagSlug),
slug: BlogTagSlug,
title: BlogTagPosts.get("tag.title"),
},
})
})
}
if (BlogCategorySlugs.length > 0) {
BlogCategorySlugs.forEach((BlogCategorySlug) => {
createPage({
path: `/category/${BlogCategorySlug}`,
component: BlogCategoryPostsTemplate,
context: {
group: BlogCategoryPosts.get(BlogCategorySlug),
slug: BlogCategorySlug,
title: BlogCategoryPosts.get("category.title"),
},
})
})
}
}
Where do I start?
I have tried upgrading packages, stripping out the config option in the config file, and deleting node_modules, package-lock.json and reinstalling the packages. I do run into errors when trying a normal npm i command, but that is fixed by adding the flag -legacy-peer-deps.
I also got this same error, but I got this after upgrading my WordPress plugins. Im pretty sure this is due to the new version 13.x of WP GraphQL. You can fix this by downgrading the WP GraphQL WordPress plugin to version 12.3. You can grab that v12 version here. Also more info on this issue and if a fix is implemented it will probably be updated here.
Had exactly the same issue.
Downgrading WP GraphQL to version 1.12.3 fixed this for me.
Here's the link wp-graphql.1.12.3
UPDATE
It seems that I can't get the bucket's reference correctly or use the bucket functions.
When I do
// Get the photos' bucket
const bucket = gcs.bucket("photos");
console.log("deleting the bucket");
// Delete bucket
bucket.delete();
I get the error too (but the bucket exists in my project)
Error: There is an account problem for the requested project.
at new ApiError (/workspace/node_modules/#google-cloud/common/build/src/util.js:58:15)
at Util.parseHttpRespBody (/workspace/node_modules/#google-cloud/common/build/src/util.js:193:38)
at Util.handleResp (/workspace/node_modules/#google-cloud/common/build/src/util.js:134:117)
at retryRequest (/workspace/node_modules/#google-cloud/common/build/src/util.js:432:22)
at onResponse (/workspace/node_modules/retry-request/index.js:206:7)
at /workspace/node_modules/teeny-request/build/src/index.js:233:13
at process._tickCallback (internal/process/next_tick.js:68:7)
PROBLEM
I am having this error:
{
"error": {
"code": 401,
"message": "There is an account problem for the requested project.",
"errors": [
{
"message": "There is an account problem for the requested project.",
"domain": "global",
"reason": "required",
"locationType": "header",
"location": "Authorization"
}
]
}
}
PassThrough {
Unhandled error { Error: There is an account problem for the requested project
at new ApiError (/workspace/node_modules/#google-cloud/common/build/src/util.js:58:15)
_readableState:
ReadableState {
at Util.parseHttpRespBody (/workspace/node_modules/#google-cloud/common/build/src/util.js:193:38)
...
What I am doing is just:
Get a bucket reference
Get a file from the bucket with the file's path
Get metadata and the signed url of the file.
My problem is the third step. When I call those functions, I don't receive any answer... no metadata, no url.
Here is the code:
const { Storage } = require("#google-cloud/storage");
const gsc = new Storage()
const serviceAccount = require("./serviceAccount.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://my_app_id.firebaseio.com",
storageBucket: "my_app_id.appspot.com",
});
exports.validateImageDimensions = functions
.region("us-central1")
// Increased memory, decreased timeout (compared to defaults)
.runWith({ memory: "2GB", timeoutSeconds: 120 })
.https.onCall(async (data, context) => {
// Get the image's owner
const owner = context.auth.token.uid;
// Get the image's info
const { id, description, location, tags, time } = data;
// Get the photos' bucket
const bucket = gcs.bucket("photos");
// Get the file's path
const filePath = bucket.name + "/" + id;
// Get the file
const file = bucket.file(filePath);
console.log(JSON.stringify(file));
// Check if the file is a jpeg image
const metadata = await file.getMetadata(); // <------- ERROR
console.log(JSON.stringify(metadata));
const isJpgImage = metadata[0].contentType === "image/jpeg";
console.log(`Is jpeg? ${isJpgImage}`);
// Get the file's signed urls
const signedUrls = await file.getSignedUrl({
action: "read",
expires: "01-01-2100",
});
// signedUrls[0] contains the file's public URL
const publicUrl = signedUrls[0];
console.log(`publicUrl ${publicUrl}`);
console.log(`Url: ${publicUrl}`);
The file exists in my storage... and when I console.log it I can see:
{
"_eventsCount": 0,
"_events": {},
"id": "photos%2Fecbb4a5a-aa11-451f-b8af-efd3e8464a59",
"baseUrl": "/o",
"metadata": {},
"acl": { "pathPrefix": "/acl", "owners": {}, "readers": {}, "writers": {} },
"methods": {
"exists": { "reqOpts": { "qs": {} } },
"get": { "reqOpts": { "qs": {} } },
"delete": { "reqOpts": { "qs": {} } },
"getMetadata": { "reqOpts": { "qs": {} } },
"setMetadata": { "reqOpts": { "qs": {} } }
},
"bucket": {
"_eventsCount": 0,
"baseUrl": "/b",
"storage": {
"baseUrl": "https://storage.googleapis.com/storage/v1",
"projectId": "{{projectId}}",
"authClient": {
"cachedCredential": null,
"scopes": [
"https://www.googleapis.com/auth/iam",
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/devstorage.full_control"
],
"jsonContent": null,
"_cachedProjectId": null
},
"globalInterceptors": [],
"acl": {
"OWNER_ROLE": "OWNER",
"READER_ROLE": "READER",
"WRITER_ROLE": "WRITER"
},
...
Any ideas? I have been googling for hours but no answer.
Also, my storage rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
function isSignedIn() {
return request.auth.uid != null;
}
match /photos/{photo} {
function hasValidSize() {
// Max. photo size = 30MB (For all dimensions)
return request.resource.size < 30 * 1024 * 1024;
}
function isImage() {
return request.resource.contentType.matches("image/.*");
}
allow read: if true;
allow write: if isSignedIn() && isImage() && hasValidSize();
}
}
}
I'm working on a gatsby site using gatsby-source-wordpress to source posts for the blog. However, if any of the WordPress posts do not include a featured image this causes the build to fail. I understand that this is expected behavior.
Here is the build error I am seeing:
29 | {posts.map(({ node: post }, index) => (
30 | <li key={post.id} {...post}>
> 31 | <Img fixed={post.featured_media.localFile.childImageSharp.fixed} />
| ^
32 | <p>
33 | <Link to={`/insights/${post.slug}`}>
34 | {post.title}
WebpackError: TypeError: Cannot read property 'localFile' of null
This is caused by the resulting query, which is returning a null result in the second node because there is no featured image on the post:
{
"data": {
"allWordpressPost": {
"edges": [
{
"node": {
"id": "28ec9054-5b05-5f94-adcb-dcbfc14659b1",
"featured_media": {
"id": "f12d613b-e544-560b-a86f-cd0a7f87801e",
"localFile": {
"id": "7fca2893-ff80-5270-9765-d17d3dc21ac2",
"url": "https://www.mycustomdomain.com/wp-content/uploads/2020/01/some-featured-image.jpg"
}
}
}
},
{
"node": {
"id": "91a236ed-39d5-5efc-8bed-290d8344b660",
"featured_media": null
}
}
]
}
}
}
How I would like to fix:
As an ideal solution, I would like to use schema customization to set a default image if there is no featured image in WordPress. But I am at a total loss how to correctly do so. I am working from this documentation to guide me, but I'm just not getting my head wrapped around it properly.
A similar working example:
Tag data is similar to featured images in that the query returns null if the post has no tags. However I am able to set a default undefined tag using createResolvers like so:
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
wordpress__POST: {
tags: {
resolve(source, args, context, info) {
const { tags } = source
if (tags === null || (Array.isArray(tags) && !tags.length)) {
return [
{
id: 'undefined',
name: 'undefined',
slug: 'undefined',
}
]
} else {
return info.originalResolver(source, args, context, info)
}
},
},
},
}
createResolvers(resolvers)
}
And this works as shown in the following query results:
{
"data": {
"allWordpressPost": {
"edges": [
{
"node": {
"id": "28ec9054-5b05-5f94-adcb-dcbfc14659b1",
"tags": [
{
"id": "undefined"
}
]
}
},
{
"node": {
"id": "91a236ed-39d5-5efc-8bed-290d8344b660",
"tags": [
{
"id": "50449e18-bef7-566a-a3eb-9f7990084afb"
},
{
"id": "8635ff58-2997-510a-9eea-fe2b88f30781"
},
{
"id": "97029bee-4dec-5198-95af-8464393f71e3"
}
]
}
}
]
}
}
}
What I tried for images (isn't working...)
When it comes to nested nodes and image files I'm at a total loss. I am heading in the following direction based on this article and this code example, but so far it isn't working:
exports.createResolvers = ({
actions,
cache,
createNodeId,
createResolvers,
store,
reporter,
}) => {
const { createNode } = actions
const resolvers = {
wordpress__POST: {
featured_media: {
type: `File`,
resolve(source, args, context, info) {
return createRemoteFileNode({
url: 'https://www.mycustomdomain.com/wp-content/uploads/2017/05/placeholder.png',
store,
cache,
createNode,
createNodeId,
reporter,
})
},
},
},
}
createResolvers(resolvers)
}
I realize the above code does not have an if else statement, so the expectation is that all featured images would be replaced by the placeholder image. However the resulting GraphQL query is unaffected (as shown at top).
Can anyone point me in the right direction here? I can't seem to wrap my head around what information I can find out there.
WebpackError: TypeError: Cannot read property 'localFile' of null
'localFile' of null means that nulled is a parent of localfile - featured_media ... you can see that in results:
"featured_media": null
... so you're trying to fix localfile while you should work on featured_media level
why?
You can easily render conditionally [in react] what you need (placeholde, component) on nulled nodes ... why at all you're trying to fix graphql response?
In GraphiQL at http://localhost:8080/graphiql, I'm using this query:
{
instant_message(fromID: "1"){
fromID
toID
msgText
}
}
I'm getting this response:
{
"data": {
"instant_message": {
"fromID": null,
"toID": null,
"msgText": null
}
},
"errors": [
{
"message": "Resolve function for \"instant_message.fromID\" returned undefined",
"locations": [
{
"line": 3,
"column": 5
}
]
},
{
"message": "Resolve function for \"instant_message.toID\" returned undefined",
"locations": [
{
"line": 4,
"column": 5
}
]
},
{
"message": "Resolve function for \"instant_message.msgText\" returned undefined",
"locations": [
{
"line": 5,
"column": 5
}
]
}
]
}
I tried to set up my system according to the examples found here:
https://medium.com/apollo-stack/tutorial-building-a-graphql-server-cddaa023c035#.s7vjgjkb7
Looking at that article, it doesn't seem to be necessary to set up individual resolvers for string fields, but I must be missing something.
What is the correct way to update my resolvers so as to return results from string fields? Example code would be greatly appreciated!
Thanks very much in advance to all for any thoughts or info.
CONNECTORS
import Sequelize from 'sequelize';
//SQL CONNECTORS
const db = new Sequelize(Meteor.settings.postgres.current_dev_system.dbname, Meteor.settings.postgres.current_dev_system.dbuser, Meteor.settings.postgres.current_dev_system.dbpsd, {
host: 'localhost',
dialect: 'postgres',
});
db
.authenticate()
.then(function(err) {
console.log('Connection to Sequelize has been established successfully.');
})
.catch(function (err) {
console.log('Unable to connect to the Sequelize database:', err);
});
const IMModel = db.define('IM', {
id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true},
fromID: {type: Sequelize.STRING},
toID: {type: Sequelize.STRING},
msgText: {type: Sequelize.STRING}
});
IMModel.sync({force: true}).then(function () {
// Table created
return IMModel.create({
fromID: '1',
toID: '2',
msgText: 'msg set up via IMModel.create'
});
});
const IM = db.models.IM;
export {db, IM };
SCHEMA
const typeDefinitions = [`
type instant_message {
id: Int
fromID: String
toID: String
msgText: String
}
type Query {
instant_message(fromID: String, toID: String, msgText: String): instant_message
}
type RootMutation {
createInstant_message(
fromID: String!
toID: String!
msgText: String!
): instant_message
}
schema {
query: Query,
mutation: RootMutation
}
`];
export default typeDefinitions;
RESOLVERS
import * as connectors from './db-connectors';
import { Kind } from 'graphql/language';
const b = 100;
const resolvers = {
Query: {
instant_message(_, args) {
const a = 100;
return connectors.IM.find({ where: args });
}
},
RootMutation: {
createInstant_message: (__, args) => { return connectors.IM.create(args); },
},
};
export default resolvers;
When you define your GraphQLObjectTypes you need to provide a resolver for each of their fields.
You defined your instant_message with multiple fields but did not provide resolvers for each of these fields.
More over you defined the types of those field with regular typescript fields while you need to define it with GraphQL types (GraphQLInt, GraphQLString, GrapQLFloat etc..)
So defining your type should look something like this:
let instant_message = new GraphQLObjectType({
id: {
type: GraphQLInt,
resolve: (instantMsg)=> {return instantMsg.id}
}
fromID: {
type: GraphQLString,
resolve: (instantMsg)=> {return instantMsg.fromID}
}
toID: {
type: GraphQLString,
resolve: (instantMsg)=> {return instantMsg.toID}
}
msgText: {
type: GraphQLString,
resolve: (instantMsg)=> {return instantMsg.msgText}
}
})
In addition, you will need to define your Query as follows:
let Query = new GraphQLObjectType({
name: "query",
description: "...",
fields: () => ({
instant_messages: {
type: new GraphQLList(instant_message),
args: {
id: {type: GraphQLInt}
},
resolve: (root, args) => {
connectors.IM.find({ where: args })
}
}
})
})
The issue is that the query does not expect an array,
Please fix it:
type Query {
instant_message(fromID: String, toID: String, msgText: String): [instant_message]
}
Then you should make sure the resolver returns Array of objects, if it doesnt work then the resolver is not returning an Array.
Problem:
I have a number of groups that each have members that belong to different groups. Each member has a title (role) in each group.
I’m trying to list all the groups and display each member in the group and their title.
I’m using reywood:publish-composite, and everything is working except I can’t get the title of each member to display.
I think the problem is in the Template.groupMembers.helpers file
title: function() {
console.log(this.roleId); // this shows up in the console for each member
return Titles.findOne({titleId: this.roleId}); // but this doesn’t work
},
Collections:
groups {
"_id" : "xFSzAHBEps2dSKcWM",
"name" : "Generic Group",
"logo" : "generic-logo-hi.png"
}
members {
"_id" : "vyDtiaKKukZYQdFvs",
"groupId" : "xFSzAHBEps2dSKcWM",
"memberId" : "hRx8GBTyB5X8iQQ52",
"roleId" : "1"
}
Meteor.users {
"_id" : "hRx8GBTyB5X8iQQ52",
"profile" : {
"name" : "Bob Lorros"
},
}
titles {
"_id" : "bYsKpsyYtyKR8NYpm",
"titleId" : 1,
"title" : "Staff (non-voting)"
}
server/publications/publications.js
Meteor.publishComposite('groupMembers', {
find: function() {
return Groups.find({}, {
sort: {name: 1}
});
},
children: [
{
find: function() {
return Titles.find();
},
find: function(group) {
return Members.find({groupId: group._id});
},
children: [
{
find: function(member) {
return Meteor.users.find({_id: member.memberId});
}
},
]
},
]
});
client/templates/test/test.js
Template.groupMembers.helpers({
groupMembers: function() {
return Groups.find({}, {
sort: {name: 1}
});
},
members: function() {
return Members.find({groupId: this._id});
},
title: function() {
console.log(this.roleId); // this shows up in the console for each member
return Titles.findOne({titleId: this.roleId}); // but this doesn’t work
},
memberName: function() {
return Meteor.users.findOne(this.memberId);
},
});
client/templates/test/test.html
<template name="groupMembers">
<h4>Group - Members</h4>
{{#each groupMembers}}
<b>{{name}}</b><br>
{{#each members}}
{{memberName.profile.name}}
- title = {{title.title}}
<br>
{{/each}}
<br>
{{/each}}
</template>
Output :
This is the ouput
Looking at this from a completely different perspective, I actually think you could use alanning:roles to accomplish exactly what you're looking for. You can use the role as the 'title' in this case and the 'group' to replace your groups. Here's the documentation:
https://github.com/alanning/meteor-roles
Not sure but I think your second find may be overriding your first. Instead of:
find: function() {
return Titles.find();
},
find: function(group) {
return Members.find({groupId: group._id});
},
Try returning an array of cursors.
find: function() {
return [
Titles.find(),
Members.find({groupId: group._id})
];
},
I don't understand however why Titles is a child of GroupMembers when the query for titles is all titles. Did you mean to have a query there?
I think your publishComposite is causing the problem, each object in the children array should have only one find and zero or more children. Also the second parameter in your publication must be a function and not a JSON object. Try this,
Meteor.publishComposite('groupMembers', function () {
return {
find: function() {
return Groups.find({}, {
sort: {name: 1}
});
},
children: [{
find: function() {
return Titles.find();
}
},
{
find: function(group) {
return Members.find({groupId: group._id});
},
children: [{
find: function(member) {
return Meteor.users.find({_id: member.memberId});
}
}]
}]
};
});
You can also improve performance by moving Titles.find to the root level
Meteor.publishComposite('groupMembers', function () {
return [{
find: function() {
return Titles.find();
}
}, {
find: function() {
return Groups.find({}, {
sort: {name: 1}
});
},
children: [{
find: function(group) {
return Members.find({groupId: group._id});
},
children: [{
find: function(member) {
return Meteor.users.find({_id: member.memberId});
}
}]
}]
}];
});