Is it possible to reuse code from atlassian *.soy files like createRepo.soy for own plugin?

kuki July 7, 2012

I wanted to create window similar to 'Create Project' which is described in createRepo.soy template file. I created servlet which displays .soy file and at the begginin I wanted to just display copy of createRepo.soy. Servlet code looks like that:

@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        final SoyFileSet sfs = new SoyFileSet.Builder().add(new File("src/main/resources/cloneRepo/createRepo.soy")).build();
        final SoyTofu.Renderer renderer = sfs.compileToJavaObj().newRenderer("stash.page.createRepo");

        resp.setContentType("text/html");
        resp.getWriter().write(renderer.render());
    }

Probably some parameters like project are mising, but that's not important for now. With this code I get errors like that:

[WARNING] [talledLocalContainer] SEVERE: Servlet.service() for servlet plugins threw exception

[WARNING] [talledLocalContainer] com.google.template.soy.base.SoySyntaxException: In file src/main/resources/cloneRepo/createRepo.soy:11: Unrecognized function 'webResourceManager_requireResourcesForContext' (function call "webResourceManager_requireResourcesForContext('internal.page.createRepo')").

Is it possible to somehow reuse this file without modifying it completley?
Or maybe my whole approach is wrong?

1 answer

1 accepted

2 votes
Answer accepted
jhinch _Atlassian_
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
July 8, 2012

This is definitely possible but just as a note, you shouldn't consider our soy templates as a public API. We provide no guarantee that these templates won't change or that they work correctly from within a plugin.

That said, what you need to do is add the atlassian-soy-template-renderer-api as a provided dependency to your plugin

<dependency>
  <groupId>com.atlassian.soy</groupId>
  <artifactId>soy-template-renderer-api</artifactId>
  <version>1.1.1</version>
  <scope>provided</scope>
</dependency>

You can then use the SoyTemplateRenderer to render the template. All templates are based off web resources for dependency resolution of files and custom functions. All of Stash's server side templates are currently available in the com.atlassian.stash.stash-web-plugin:server-soy-templates web resource module.

So for instance to render the createRepo page you can do the following:

soyTemplateRenderer.render(response.getWriter(), "com.atlassian.stash.stash-web-plugin:server-soy-templates", "stash.page.createRepo", new HashMap(), new HashMap());

kuki July 8, 2012

Thanks for your help! I'm glat that it's possible at all :)

Unlucky when I tried to follow your suggestion I encountered some more problems.
First of all, from where in my servlet I should get soyTemplateRenderer? I looked at source code and found out that it's injected by spring and should be accesible as bean. So I did something like that:

WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
SoyTemplateRenderer soyTemplateRenderer = (SoyTemplateRenderer) context.getBean(SoyTemplateRenderer.class);

Unlucky, this doesn't work as I expected:

[WARNING] [talledLocalContainer] SEVERE: Servlet.service() for servlet plugins threw exception

[WARNING] [talledLocalContainer] java.lang.IllegalStateException: Context attribute is not of type WebApplicationContext: Root WebApplicationContext: startup date [Mon Jul 09 18:15:02 CEST 2012]; root of context hierarchy

Can you tell me what am I doing wrong or at least point me to some resources where I could find answer myself?

Colin Goudie
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
July 9, 2012

Just use constructor injection and also include the SoyTemplateRenderer in a component-import in your atlassian-plugin.xml

kuki July 10, 2012

Thanks Colin! Somehow I completely forgot about this possibility. Unlucky I have one more problem. I tried to add:

<component-import key="soyTemplateRenderer" interface="com.atlassian.soy.renderer.SoyTemplateRenderer" />

to my atlassian-plugin.xml but it seems to be not working properly. If I put this line right after plugin-info section then whole plugin seems to be no installed correctly (and it says that there is 0 modules at all).

I'm sure I'm missing something really simple, but I have no idea what it could be.

jhinch _Atlassian_
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
July 11, 2012

Was there an exception in the logs?

kuki July 12, 2012

Somehow it works now. I guess I had one more subltle bug in atlassian-plugin.xml.

I guess I'm almost there. I have - hopefully - last guestion. My code currently looks like that:

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
final Project project = projectService.findByKey("PROJECT_1");
if (project != null) {
try {
soyTemplateRenderer.render(
resp.getWriter(),
"com.atlassian.stash.stash-web-plugin:server-soy-templates",
"stash.page.createRepo",
ImmutableMap.<String, Object>of(
"project", project
)
);
} catch (SoyException e) {
e.printStackTrace()
}
}
}


I need to pass project parameter to the template, but I don't know how it should look like. I only know that it must be able to work with this:

{{param contentTitle: cav_i18n('stash.web.repository.create.repository', 'Create a Repository in {0}', $project.name) /}}

and

{foreach $webPanel in getWebPanels('stash.web.repository.create.beforefields', [ 'project' : $project ])}

I tried to put as parameter something like that:

ImmutableMap.<String, Object>of(
"project", project,
"project.name", project.getName()
)

but then I get:

[WARNING] [talledLocalContainer] com.google.template.soy.tofu.SoyTofuException: In template stash.page.createRepo: When evaluating "getWebPanels('stash.web.repository.create.beforefields', $ij.request, ['project': $project])": Error while computing function "getWebPanels('stash.web.repository.create.beforefields', $ij.request, ['project': $project])": Non-string found when expecting string value.

And for parameter like:

ImmutableMap.<String, Object>of(
"project", project.getKey(),
"project.name", project.getName()
)

I'm getting:

WARNING] [talledLocalContainer] com.google.template.soy.data.SoyDataException: Failed to evaluate key string "project.name" for put().


I tried few different variants but without success so far...

kuki July 12, 2012

I wanted to try something simplier, so I replaced "stash.page.createRepo" with "stash.page.createProject" which does not require any parameters. Unlucky it still generates errors:

[WARNING] [talledLocalContainer] com.google.template.soy.tofu.SoyTofuException: In template stash.page.createProject: When evaluating "getWebPanels('stash.web.project.create.beforefields', $ij.request)": Error while computing function "getWebPanels('stash.web.project.create.beforefields', $ij.request)": Non-string found when expecting string value.

But this mean it's not related to missing parameter, but rather to some not loaded component or whatever else. Do you have any idea what could be the reason guys?

kuki July 13, 2012

Ok, I got it now. Couple of parameters were missing, but that's it.

To check what is required it's worth to look at SoyView, InjectedDataFactory and SoyResponseBuilder (which unlucky are internal ones, so I had to rewrite part of their functionality).

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events