Skip to content

Commit 4e7263b

Browse files
starting point for file-based module CLAUDE.md
1 parent dcdc0f6 commit 4e7263b

2 files changed

Lines changed: 399 additions & 0 deletions

File tree

Lines changed: 395 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,395 @@
1+
# CLAUDE.md - File-Based Module Development Guide
2+
3+
This file provides guidance for creating and developing LabKey file-based modules.
4+
5+
## What is a File-Based Module?
6+
7+
A file-based module is a LabKey module that doesn't contain any Java code. It enables custom development without compiling, letting you directly deploy and test module resources, often without restarting the server. File-based modules support:
8+
9+
- SQL queries and views
10+
- Reports (R, JavaScript, HTML)
11+
- Custom data views
12+
- Web parts and HTML/JavaScript client-side applications
13+
- Assay definitions
14+
- ETL configurations
15+
- Pipeline definitions
16+
17+
## Module Directory Structure
18+
19+
### Development/Source Layout
20+
```
21+
myModule/
22+
├── module.properties # Module configuration (REQUIRED)
23+
├── README.md # Module documentation
24+
└── resources/ # All module resources go here
25+
├── queries/ # SQL queries and query metadata
26+
│ └── [schema_name]/ # Organize by schema
27+
│ ├── [query_name].sql
28+
│ ├── [query_name].query.xml
29+
│ └── [query_name]/ # Query-specific views
30+
│ └── [view_name].html
31+
├── reports/ # Report definitions
32+
│ └── schemas/
33+
│ └── [schema_name]/
34+
│ └── [query_name]/
35+
│ ├── [report_name].r
36+
│ ├── [report_name].rhtml
37+
│ └── [report_name].report.xml
38+
├── views/ # Custom views and web parts
39+
│ ├── [view_name].html
40+
│ └── [view_name].webpart.xml
41+
├── schemas/ # Database schema definitions
42+
│ └── dbscripts/
43+
│ ├── postgresql/
44+
│ └── sqlserver/
45+
├── web/ # JavaScript, CSS, images
46+
│ └── [moduleName]/
47+
│ ├── [moduleName].js
48+
│ └── [moduleName].css
49+
├── assay/ # Assay type definitions
50+
├── etls/ # ETL configurations
51+
├── folderTypes/ # Custom folder type definitions
52+
└── pipeline/ # Pipeline task definitions
53+
```
54+
55+
### Deployed Layout
56+
When deployed, the structure changes slightly:
57+
- `resources/` directory contents move to root level
58+
- `module.properties` becomes `config/module.xml`
59+
- Compiled code (if any) goes to `lib/`
60+
61+
## module.properties File
62+
63+
This is the **required** configuration file for your module. Place it in the module root.
64+
65+
### Required Properties
66+
```properties
67+
ModuleClass: org.labkey.api.module.SimpleModule
68+
Name: myModule
69+
```
70+
71+
### Recommended Properties
72+
```properties
73+
ModuleClass: org.labkey.api.module.SimpleModule
74+
Name: myModule
75+
Label: My Custom Module
76+
Description: A file-based module for custom queries, reports, and views.\
77+
Multi-line descriptions can span multiple lines using backslash continuation.
78+
Version: 1.0.0
79+
Author: Your Name <your.email@example.com>
80+
Organization: Your Organization
81+
OrganizationURL: https://example.com
82+
License: Apache 2.0
83+
LicenseURL: https://www.apache.org/licenses/LICENSE-2.0
84+
Maintainer: Your Name <your.email@example.com>
85+
RequiredServerVersion: 23.11
86+
```
87+
`Name` should usually be the same as the directory name, especially for file-based modules.
88+
89+
### Additional Properties
90+
- **SchemaVersion**: Version number for SQL schema upgrade scripts (e.g., `1.00`)
91+
- **ManageVersion**: Boolean (true/false) for schema version management
92+
- **BuildType**: "Development" or "Production"
93+
- **SupportedDatabases**: "pgsql" or "mssql" (comma-separated)
94+
- **URL**: Homepage URL for the module
95+
96+
### Auto-Generated Properties (Don't Set)
97+
These are set during build: BuildNumber, BuildOS, BuildPath, BuildTime, BuildUser, EnlistmentId, ResourcePath, SourcePath, VcsRevision, VcsURL
98+
99+
## Creating Web Parts
100+
101+
Web parts are HTML views that can be added to LabKey pages.
102+
103+
### Basic Web Part Structure
104+
105+
**File**: `resources/views/myWebPart.html`
106+
```html
107+
<div class="labkey-module-content">
108+
<h2>My Web Part</h2>
109+
<p>Content goes here</p>
110+
</div>
111+
112+
<script type="text/javascript" nonce="<%=scriptNonce%>">
113+
// JavaScript code here
114+
// IMPORTANT: Always include nonce="<%=scriptNonce%>" for CSP compliance
115+
</script>
116+
```
117+
118+
**Configuration**: `resources/views/myWebPart.webpart.xml`
119+
```xml
120+
<?xml version="1.0" encoding="UTF-8"?>
121+
<webpart xmlns="http://labkey.org/data/xml/webpart">
122+
<name>My Web Part</name>
123+
<description>Description of what this web part does</description>
124+
<location>body</location>
125+
</webpart>
126+
```
127+
128+
### Important: Content Security Policy (CSP)
129+
130+
LabKey enforces CSP, so **all inline scripts must include the nonce attribute**:
131+
```html
132+
<script type="text/javascript" nonce="<%=scriptNonce%>">
133+
// Your code here
134+
</script>
135+
```
136+
137+
Without the nonce, your inline scripts will be blocked by the browser.
138+
139+
### Template Variables
140+
141+
LabKey automatically substitutes the following variables in HTML view files (use `<%=variableName%>` syntax):
142+
143+
- **scriptNonce**: CSP nonce for inline scripts. **Required for all `<script>` tags** to comply with Content Security Policy.
144+
```html
145+
<script type="text/javascript" nonce="<%=scriptNonce%>">
146+
```
147+
148+
- **contextPath**: The web application's context path (e.g., `/labkey`). Use for building URLs to server resources.
149+
```javascript
150+
var url = '<%=contextPath%>' + '/someResource.js';
151+
```
152+
153+
- **containerPath**: The current container's path (e.g., `/MyProject/MyFolder`). Use for building container-specific URLs.
154+
```javascript
155+
var containerUrl = '<%=contextPath%>' + '<%=containerPath%>';
156+
```
157+
158+
- **wrapperDivId**: A unique ID for the wrapper div containing the view (format: `ModuleHtmlView_<uniqueID>`). Useful for scoping JavaScript or CSS to a specific view instance.
159+
```javascript
160+
var wrapper = document.getElementById('<%=wrapperDivId%>');
161+
```
162+
163+
- **id**: The web part's row ID (or `-1` if not rendered as a web part). Use to identify specific web part instances.
164+
```javascript
165+
var webPartId = <%=id%>; // Note: no quotes, this is a number
166+
```
167+
168+
- **webpartContext**: A JSON string containing configuration for the web part, including:
169+
- `wrapperDivId`: The wrapper div ID
170+
- `id`: The web part row ID
171+
- `properties`: An object containing the web part's custom properties (from webpart.xml configuration)
172+
- Any additional properties set on the web part
173+
174+
```javascript
175+
var config = JSON.parse('<%=webpartContext%>');
176+
console.log('Web part ID:', config.id);
177+
console.log('Properties:', config.properties);
178+
```
179+
180+
**Note**: The entire view is automatically wrapped in a div with the `wrapperDivId`, so you don't need to create it yourself.
181+
182+
## Using LabKey JavaScript APIs
183+
184+
LabKey provides a comprehensive JavaScript API for interacting with the server.
185+
186+
**Complete API Reference**: For the full JavaDoc-style API documentation, see https://labkey.github.io/labkey-api-js/
187+
188+
### Accessing User and Container Information
189+
190+
User and container information is automatically rendered into the page:
191+
192+
```javascript
193+
// Access current user information
194+
console.log('User ID:', LABKEY.user.id);
195+
console.log('Display Name:', LABKEY.user.displayName);
196+
console.log('Email:', LABKEY.user.email);
197+
console.log('Is Admin:', LABKEY.user.isAdmin);
198+
console.log('Is Guest:', LABKEY.user.isGuest);
199+
200+
// Access current container/folder information
201+
console.log('Container ID:', LABKEY.container.id);
202+
console.log('Container Path:', LABKEY.container.path);
203+
console.log('Container Name:', LABKEY.container.name);
204+
```
205+
206+
### Common LABKEY JavaScript Objects
207+
208+
- **LABKEY.ActionURL**: Build URLs to LabKey controllers and actions
209+
- **LABKEY.Ajax**: Make AJAX requests to LabKey APIs
210+
- **LABKEY.Query**: Execute SQL queries and retrieve data
211+
- **LABKEY.Security**: Access user permissions and security info
212+
- **LABKEY.Utils**: Utility functions for common tasks
213+
214+
### Example: Querying Data
215+
216+
```javascript
217+
LABKEY.Query.selectRows({
218+
schemaName: 'lists',
219+
queryName: 'MyList',
220+
success: function(data) {
221+
console.log('Rows:', data.rows);
222+
},
223+
failure: function(error) {
224+
console.error('Query failed:', error);
225+
}
226+
});
227+
```
228+
229+
## Creating SQL Queries
230+
231+
Place SQL query files in `resources/queries/[schema_name]/`.
232+
233+
### Basic Query
234+
235+
**File**: `resources/queries/core/Users.sql`
236+
```sql
237+
SELECT
238+
UserId,
239+
DisplayName,
240+
Email,
241+
Active
242+
FROM core.Users
243+
WHERE Active = TRUE
244+
ORDER BY DisplayName
245+
```
246+
247+
### Query Metadata
248+
249+
**File**: `resources/queries/core/Users.query.xml`
250+
```xml
251+
<?xml version="1.0" encoding="UTF-8"?>
252+
<query xmlns="http://labkey.org/data/xml/query">
253+
<metadata>
254+
<columns>
255+
<column columnName="UserId">
256+
<description>Unique user identifier</description>
257+
</column>
258+
<column columnName="DisplayName">
259+
<description>User's display name</description>
260+
</column>
261+
</columns>
262+
</metadata>
263+
</query>
264+
```
265+
266+
## Deployment and Testing
267+
268+
### Location
269+
270+
File-based modules are deployed to:
271+
```
272+
<LABKEY_ROOT>/build/deploy/externalModules/[moduleName]/
273+
```
274+
275+
**IMPORTANT**: Back up your module before running `gradlew cleanBuild` as the build directory may be deleted.
276+
277+
### Enabling the Module
278+
279+
1. Navigate to your target folder in LabKey
280+
2. Go to **Folder > Management**
281+
3. Click the **Folder Type** tab
282+
4. Check your module's checkbox under "Modules"
283+
5. Click **Update Folder**
284+
285+
### Hot Deployment
286+
287+
Most changes to file-based module resources don't require a server restart:
288+
- ✅ HTML/JavaScript changes in views: No restart needed
289+
- ✅ Query definition changes: No restart needed
290+
- ✅ Report updates: No restart needed
291+
- ⚠️ module.properties changes: Restart required
292+
- ⚠️ Adding new resource types: May require restart
293+
294+
Simply refresh your browser to see changes.
295+
296+
## Best Practices
297+
298+
### Security
299+
- Always use `nonce="<%=scriptNonce%>"` for inline scripts
300+
- Escape user-supplied data before rendering in HTML
301+
- Use LABKEY.Utils.encodeHtml() to prevent XSS
302+
- Never expose sensitive data in client-side code
303+
304+
### Code Organization
305+
- Keep JavaScript in separate files under `resources/web/[moduleName]/`
306+
- Use meaningful names for queries, views, and reports
307+
- Group related queries by schema
308+
- Document your queries with .query.xml metadata files
309+
310+
### Performance
311+
- Use query metadata to hide unnecessary columns
312+
- Limit result sets with WHERE clauses
313+
- Create database indexes for frequently queried columns
314+
- Cache expensive queries when possible
315+
316+
### Maintenance
317+
- Version your module.properties appropriately
318+
- Document breaking changes in your README
319+
- Test on both PostgreSQL and SQL Server if supporting both
320+
- Keep module.properties up to date with RequiredServerVersion
321+
322+
## Common Patterns
323+
324+
### Creating a Dashboard Web Part
325+
326+
```html
327+
<div class="labkey-module-content">
328+
<h2>My Dashboard</h2>
329+
<div id="dashboard-content">Loading...</div>
330+
</div>
331+
332+
<script type="text/javascript" nonce="<%=scriptNonce%>">
333+
(function() {
334+
// Parse web part configuration
335+
var config = JSON.parse('<%=webpartContext%>');
336+
console.log('Web part ID:', config.id);
337+
console.log('Custom properties:', config.properties);
338+
339+
// Query data in the current container
340+
LABKEY.Query.selectRows({
341+
containerPath: '<%=containerPath%>',
342+
schemaName: 'core',
343+
queryName: 'Users',
344+
success: function(data) {
345+
var html = '<ul>';
346+
data.rows.forEach(function(row) {
347+
html += '<li>' + LABKEY.Utils.encodeHtml(row.DisplayName) + '</li>';
348+
});
349+
html += '</ul>';
350+
351+
// Use wrapperDivId to scope to this specific view instance
352+
var wrapper = document.getElementById('<%=wrapperDivId%>');
353+
var contentDiv = wrapper.querySelector('#dashboard-content');
354+
contentDiv.innerHTML = html;
355+
}
356+
});
357+
})();
358+
</script>
359+
```
360+
361+
### Adding Custom Button Actions
362+
363+
```html
364+
<div class="labkey-module-content">
365+
<button id="myButton" class="labkey-button">Click Me</button>
366+
</div>
367+
368+
<script type="text/javascript" nonce="<%=scriptNonce%>">
369+
(function() {
370+
document.getElementById('myButton').addEventListener('click', function() {
371+
LABKEY.Utils.alert('Button Clicked', 'You clicked the button!');
372+
});
373+
})();
374+
</script>
375+
```
376+
377+
## Documentation Resources
378+
379+
For more information, see:
380+
- Simple Modules Overview: https://www.labkey.org/Documentation/wiki-page.view?name=simpleModules
381+
- File-Based Module Tutorial: https://www.labkey.org/Documentation/wiki-page.view?name=moduleqvr
382+
- JavaScript API Documentation: https://labkey.github.io/labkey-api-js/
383+
- Module Directory Structures: https://www.labkey.org/Documentation/wiki-page.view?name=moduleDirectoryStructures
384+
- Query Development: https://www.labkey.org/Documentation/wiki-page.view?name=addSQLQuery
385+
386+
## Quick Start Checklist
387+
388+
- [ ] Create module directory in `build/deploy/externalModules/`
389+
- [ ] Add `module.properties` with required fields
390+
- [ ] Create `resources/` directory structure
391+
- [ ] Add at least one view or query
392+
- [ ] Enable module in a test folder
393+
- [ ] Test functionality in browser
394+
- [ ] Document usage in README.md
395+
- [ ] Back up module outside build directory

0 commit comments

Comments
 (0)