Lib-menu headless

Enonic version: 7.8.0
OS: OSX

Hello and happy new year,

We are working on a headless implementation and it’s going very well.
We use the menu library in our previous implementation to render the menu and get the structure of the menu.

Now I would like to use this in an enonic HTTP service. But when I run the function: getMenuTree()
I get an empty result. I tried running it in different contexts and with different parameters.

But no matter what I try I get an empty result. Is it possible to retrieve this structure in a HTTP service?
Or should I look at another direction.

Any tips would help me a bunch!

Kind regards,
Mark

We had a very similar discussion in a Slack thread in our community slack, but I’m not sure how it ended up. Maybe it’s worth asking the original author how he solved it.

Just took a look at the lib-menu code.
Looks like it returns an empty menu if the content is not set.

I don’t think it would be difficult to change the lib to work headless.
Want me to create a feature request to add headless support?

1 Like

If that’s not too much trouble that would be great!
Thank you.

Kind regards,
Mark

I did not know there was a community slack, i’ll definitely join.
Thanks!

Got a tip by the teach lead at our office:
If you are using headless previusly it might be simpler is extend Gullotine API by exposing a getMenu field?
The correct context is set so works without any changes to lib-menu.

Hi @Markvds,

I did not figure out how to do that in HTTP service for now. But it can be done in Controller. Below you can find the example how to extend Guillotine, for instance add getMenuItems field to some type.

const guillotineLib = require("/lib/guillotine");
const graphqlPlaygroundLib = require("/lib/graphql-playground");
const menuLib = require("/lib/menu");
const graphQlLib = require("/lib/graphql");
​
//──────────────────────────────────────────────────────────────────────────────
// Constants
//──────────────────────────────────────────────────────────────────────────────
const CORS_HEADERS = {
    "Access-Control-Allow-Headers": "Content-Type",
    "Access-Control-Allow-Methods": "POST, OPTIONS",
    "Access-Control-Allow-Origin": "*",
};
​
function resolveMenuItems() {
    // to do some transformation of menuItems if needed
    return menuLib.getMenuTree(1);
}
​
//──────────────────────────────────────────────────────────────────────────────
// Methods
//──────────────────────────────────────────────────────────────────────────────
exports.options = function () {
    return {
        contentType: "text/plain;charset=utf-8",
        headers: CORS_HEADERS,
    };
};
​
exports.get = function (req) {
    if (req.webSocket) {
        return {
            webSocket: {
                data: guillotineLib.createWebSocketData(req),
                subProtocols: ['graphql-ws']
            }
        };
    }
​
    let body = graphqlPlaygroundLib.render();
    return {
        contentType: "text/html; charset=utf-8",
        body: body,
    };
};
​
exports.post = function (req) {
    let input = JSON.parse(req.body);
​
    let params = {
        query: input.query,
        variables: input.variables,
        schemaOptions: {
            creationCallbacks: {
                '<CONTENTENT_TYPE_NAME>': function (context, params) {
                    params.fields.getMenuItems = {
                        type: graphQlLib.Json,
                        resolve: function (env) {
                            return resolveMenuItems();
                        }
                    }
                }
            }
        }
    };
​
    return {
        contentType: 'application/json',
        headers: CORS_HEADERS,
        body: guillotineLib.execute(params)
    };
};
​
exports.webSocketEvent = guillotineLib.initWebSockets();

Also you can add some parameter to context argument of the execute function, for instance:

exports.post = function (req) {
    let input = JSON.parse(req.body);

    let params = {
        query: input.query,
        variables: input.variables,
        context: {
           menus: menuLib.getMenuTree(1)
        }
    };

    return {
        contentType: 'application/json',
        body: guillotineLib.execute(params)
    };
};

this parameter will be available for any field in the resolve function using env.context, for instance, env.context.menus.

I hope this information will be useful for you.

Best regards,
Anatol

1 Like

Hi Mark. We are making the lib more flexible by adding another parameter: Support passing in root content ID Β· Issue #57 Β· enonic/lib-menu Β· GitHub

1 Like

I’m going to try this! Thank you so much!

This sounds perfect, passing a context or content id makes the lib way more flexible.