Integration examples¶
These examples assume Central has already issued an acugis_session value through its login flow. They use the reserved documentation host https://reports.example.test and placeholders only. Keep the token in an environment variable or cookie jar, never in source control.
The workflow is: discover permitted reports, load one report's expanded configuration, then execute it. Replace sales-summary, ReportingDB, and region with values returned by your service.
curl¶
export BASE_URL='https://reports.example.test'
export ACUGIS_SESSION='<opaque-session-token>'
# Public registry (no authentication gate).
curl --fail-with-body \
"$BASE_URL/jasper-report-services/api/reports"
# Session-filtered discovery.
curl --fail-with-body \
--cookie "acugis_session=$ACUGIS_SESSION" \
"$BASE_URL/jasper-report-services/api/discovery"
# Expanded definition and dynamic parameter options.
curl --fail-with-body \
--cookie "acugis_session=$ACUGIS_SESSION" \
--get \
--data-urlencode 'report=sales-summary' \
"$BASE_URL/jasper-report-services/reports/get_report_config.php"
# Execute to PDF. Repeat --data-urlencode for multi-value parameters.
curl --fail-with-body --location \
--cookie "acugis_session=$ACUGIS_SESSION" \
--get \
--data-urlencode '_repName=sales-summary' \
--data-urlencode '_dataSource=ReportingDB' \
--data-urlencode '_repFormat=pdf' \
--data-urlencode 'region=North' \
--output sales-summary.pdf \
"$BASE_URL/jasper-report-services/reports/execute_report.php"
For a cookie jar populated by an existing Central login exchange, replace each --cookie with --cookie cookie.jar --cookie-jar cookie.jar.
JavaScript (Node.js 18+)¶
import { writeFile } from "node:fs/promises";
const base = process.env.BASE_URL ?? "https://reports.example.test";
const token = process.env.ACUGIS_SESSION;
if (!token) throw new Error("Set ACUGIS_SESSION");
const headers = { Cookie: `acugis_session=${token}` };
const service = `${base}/jasper-report-services`;
async function checkedJson(url) {
const response = await fetch(url, { headers });
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
return response.json();
}
const discovery = await checkedJson(`${service}/api/discovery`);
const item = discovery.items.find(({ id }) => id === "sales-summary");
if (!item) throw new Error("Report is not visible");
const configUrl = new URL(`${service}/reports/get_report_config.php`);
configUrl.searchParams.set("report", item.id);
const config = await checkedJson(configUrl);
const executeUrl = new URL(`${service}/reports/execute_report.php`);
executeUrl.searchParams.set("_repName", config.report);
executeUrl.searchParams.set("_dataSource", config.datasource);
executeUrl.searchParams.set("_repFormat", "pdf");
executeUrl.searchParams.append("region", "North");
const output = await fetch(executeUrl, { headers });
if (!output.ok) throw new Error(`${output.status}: ${await output.text()}`);
await writeFile("sales-summary.pdf", Buffer.from(await output.arrayBuffer()));
console.log("saved sales-summary.pdf");
In browser code use same-origin URLs and credentials: "same-origin"; JavaScript cannot set the Cookie header itself.
Python¶
import os
from pathlib import Path
import requests
base = os.getenv("BASE_URL", "https://reports.example.test")
token = os.environ["ACUGIS_SESSION"]
service = f"{base}/jasper-report-services"
session = requests.Session()
session.cookies.set("acugis_session", token)
discovery = session.get(f"{service}/api/discovery", timeout=30)
discovery.raise_for_status()
item = next(row for row in discovery.json()["items"] if row["id"] == "sales-summary")
config_response = session.get(
f"{service}/reports/get_report_config.php",
params={"report": item["id"]},
timeout=30,
)
config_response.raise_for_status()
config = config_response.json()
# A list of tuples preserves repeated keys for multi-value Jasper parameters.
params = [
("_repName", config["report"]),
("_dataSource", config["datasource"]),
("_repFormat", "pdf"),
("region", "North"),
]
with session.get(
f"{service}/reports/execute_report.php",
params=params,
timeout=600,
stream=True,
) as response:
response.raise_for_status()
with Path("sales-summary.pdf").open("wb") as output:
for chunk in response.iter_content(1024 * 64):
output.write(chunk)
Go¶
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
)
type discovery struct {
Items []struct{ ID string `json:"id"` } `json:"items"`
}
type reportConfig struct {
Report string `json:"report"`
Datasource string `json:"datasource"`
}
func request(client *http.Client, token, rawURL string) (*http.Response, error) {
req, err := http.NewRequest(http.MethodGet, rawURL, nil)
if err != nil { return nil, err }
req.AddCookie(&http.Cookie{Name: "acugis_session", Value: token})
return client.Do(req)
}
func main() {
base := os.Getenv("BASE_URL")
if base == "" { base = "https://reports.example.test" }
token := os.Getenv("ACUGIS_SESSION")
if token == "" { panic("set ACUGIS_SESSION") }
service := base + "/jasper-report-services"
client := &http.Client{}
res, err := request(client, token, service+"/api/discovery")
if err != nil { panic(err) }
defer res.Body.Close()
if res.StatusCode != http.StatusOK { panic(res.Status) }
var catalog discovery
if err := json.NewDecoder(res.Body).Decode(&catalog); err != nil { panic(err) }
reportID := ""
for _, item := range catalog.Items {
if item.ID == "sales-summary" { reportID = item.ID }
}
if reportID == "" { panic("report is not visible") }
configURL := service + "/reports/get_report_config.php?report=" + url.QueryEscape(reportID)
res, err = request(client, token, configURL)
if err != nil { panic(err) }
defer res.Body.Close()
if res.StatusCode != http.StatusOK { panic(res.Status) }
var config reportConfig
if err := json.NewDecoder(res.Body).Decode(&config); err != nil { panic(err) }
query := url.Values{}
query.Set("_repName", config.Report)
query.Set("_dataSource", config.Datasource)
query.Set("_repFormat", "pdf")
query.Add("region", "North")
res, err = request(client, token, service+"/reports/execute_report.php?"+query.Encode())
if err != nil { panic(err) }
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
body, _ := io.ReadAll(res.Body)
panic(fmt.Sprintf("%s: %s", res.Status, body))
}
file, err := os.Create("sales-summary.pdf")
if err != nil { panic(err) }
defer file.Close()
if _, err := io.Copy(file, res.Body); err != nil { panic(err) }
}
PHP¶
<?php
declare(strict_types=1);
$base = getenv('BASE_URL') ?: 'https://reports.example.test';
$token = getenv('ACUGIS_SESSION');
if (!$token) {
throw new RuntimeException('Set ACUGIS_SESSION');
}
$service = rtrim($base, '/') . '/jasper-report-services';
function get(string $url, string $token): string {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_TIMEOUT => 600,
CURLOPT_HTTPHEADER => ['Cookie: acugis_session=' . $token],
]);
$body = curl_exec($ch);
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($body === false || $status < 200 || $status >= 300) {
throw new RuntimeException($error !== '' ? $error : "HTTP $status: $body");
}
return $body;
}
$discovery = json_decode(get("$service/api/discovery", $token), true, flags: JSON_THROW_ON_ERROR);
$item = null;
foreach ($discovery['items'] as $candidate) {
if (($candidate['id'] ?? '') === 'sales-summary') {
$item = $candidate;
break;
}
}
if ($item === null) {
throw new RuntimeException('Report is not visible');
}
$config = json_decode(
get("$service/reports/get_report_config.php?" . http_build_query(['report' => $item['id']]), $token),
true,
flags: JSON_THROW_ON_ERROR
);
$query = http_build_query([
'_repName' => $config['report'],
'_dataSource' => $config['datasource'],
'_repFormat' => 'pdf',
'region' => 'North',
]);
$pdf = get("$service/reports/execute_report.php?$query", $token);
if (file_put_contents('sales-summary.pdf', $pdf) === false) {
throw new RuntimeException('Could not write output');
}
PHP's http_build_query() does not represent repeated keys conveniently. For multi-value parameters, build and URL-encode each key=value pair separately.