- Prerequisites
- Overview
- Notes on Conventions
- Angular Build Process Automation
- Web application for Angular Path Location Strategy
- OpenAPI-based client code generation
isc.ipm.js requires InterSystems IRIS Data Platform 2018.1 or later.
Installation is done via the Community Package Manager:
zpm "install isc.ipm.js"
isc.ipm.js enables modern web application development, deployment, and packaging for applications built on InterSystems IRIS and using the InterSystems Package Manager. Our pilot use case is Angular, but we would love to collaborate with the community to build out similar features for other tools.
There are three parts to this:
- Angular build process automation within the package lifecycle
- Web application handling for Angular's path location strategy
- Generation of client code from OpenAPI specifications, which isc.rest can generate easily
For a simple example of all of these things working together, see isc.perf.ui and particularly its module.xml
By convention we put Angular applications in an "ng" subdirectory relative to the package root, and normally the built UI will end up in dist/app-name, where app-name is your application's npm package name. Given an Angular application named "app-name" the folder structure will be:
/ng
/app-name
/node_modules
/dist
app-name
*.js
*.css
index.html
/src
angular.json
package.json
package-lock.json
Automated build processes in this tool run npm ci for predictability and repeatability, and this requires package-lock.json. As such, package-lock.json must be committed to source control and treated as code.
isc.ipm.js will automatically run npm ci
and ng build
on your Angular UI as part of the InterSystems package manager lifecycle. Note that this requires an appropriate node and npm version to be installed for your target Angular version.
In module.xml, define a resource pointing to your Angular application root with ProcessorClass set to pkg.isc.ipm.js.angular.processor:
<Resource Name="/ng/app-name" ProcessorClass="pkg.isc.ipm.js.angular.processor">
<Attribute Name="baseHref">/csp/${namespace}/app-name</Attribute>
</Resource>
After the build completes, the hash of package.json and package-lock.json are stored, and npm ci
will not run again unless those change (or you indicate that install should be forced).
The angular build process accepts two flags (in relevant zpm module lifecycle commands) to control its behavior:
-DAngular.NoBuild=1
will suppress the npm install and Angular build (useful, for example, to load in updates to classes only)-DAngular.ForceInstall=1
will force runningnpm ci
even if it seems unnecessary (that is, package.json and package-lock.json are unchanged)
When you package your solution (e.g., via zpm "your-app publish"
) the built Angular UI will be included, so the build will not need to run for clients downloading your package from a registry (e.g., using zpm-registry).
npm ci
can be slow sometimes, which is why it is only called when necessary. ng build
should generally be relatively fast. If it seems to get stuck or generally takes longer than you expect, consider increasing --max-old-space-size via the NODE_OPTIONS environment variable. That is, set NODE_OPTIONS=--max-old-space-size=8192
or some other reasonable value; see this StackOverflow post for some relevant discussion on how to determine a reasonable value.
To make URLs prettier and simpler for users, Angular applications may - and really should - use the "Path Location Strategy". Normally this requires some additional webserver configuration. isc.ipm.js provides a CSP-based alternative to that webserver configuration.
To use it, add a resource in module.xml using the package manager's built-in CSPApplication processor and targeting the location of your built Angular UI (e.g., /ng/app-name/dist/app-name). This application should have DispatchClass
set to pkg.isc.ipm.js.angular.pathLocationHandler
and a Directory
defined.
For example:
<Resource Name="/ng/app-name/dist/app-name" ProcessorClass="CSPApplication" Generated="true">
<Attribute Name="DispatchClass">pkg.isc.ipm.js.angular.pathLocationHandler</Attribute>
<Attribute Name="Directory">${cspdir}${namespace}/app-name</Attribute>
<Attribute Name="Url">/csp/${namespace}/app-name</Attribute>
<Attribute Name="ServeFiles">1</Attribute>
<Attribute Name="Recurse">1</Attribute>
<Attribute Name="ServeFilesTimeout">0</Attribute>
</Resource>
Under the hood, pkg.isc.ipm.js.angular.pathLocationHandler
is a simple %CSP.REST
subclass that returns either a requested asset (.js, .css, image, etc.) or otherwise index.html. This provides an equivalent to web server configuration otherwise needed to serve an Angular UI that uses the path location strategy.
isc.rest can generate OpenAPI specs for APIs defined using it. isc.ipm.js provides the glue between isc.rest and openapi-generator, a popular open source tool that provides support for a wide variety of clients.
Again focusing on the Angular use case, this can be used to generate a file (say, openapi.json) and then process it using openapi-generator to generate Angular services for accessing the REST API and models repesenting the types it returns and accepts.
For example:
<Resource Name="/api/openapi.json" ProcessorClass="pkg.isc.ipm.js.openApiProcessor">
<Attribute Name="DispatchClass">myapp.rest.Handler</Attribute>
<Attribute Name="Url">/csp/${namespace}/my-app/api</Attribute>
<Attribute Name="BaseUrl">/api</Attribute>
<Attribute Name="TargetFolder">/ng/my-app/src/app/generated</Attribute>
<Attribute Name="Generator">typescript-angular</Attribute>
<Attribute Name="AdditionalProperties">paramNaming=original,queryParamObjectFormat=key,useSingleRequestParameter=true,ngVersion=13.3.4</Attribute>
</Resource>
Where:
DispatchClass
is the name of your%pkg.isc.rest.handler
subclassUrl
is the web application path through which that REST handler is being servedBaseUrl
is the URL to use as the base endpoint for accessing the API; in this case, a relative path (/api
) is used because the Angular application lives in/csp/${namespace}/my-app
TargetFolder
is the place in your Angular application where code will be generatedGenerator
is one of the very many generators supported by openapi-generator (see the full list)AdditionalProperties
vary per generator and are defined in that generator's documentation (see those for angular-typescript, for example)
Also, add the following in your module.xml (within the <Module>
element):
<LifecycleClass>pkg.isc.ipm.js.openApiModule</LifecycleClass>
Then, to generate client code for your rest API, it's just a matter of running:
zpm "my-app generate"