Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
407 changes: 406 additions & 1 deletion .vscode/eclipse-java-formatter.xml

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
{
"java.format.settings.url": ".vscode/eclipse-java-formatter.xml",
"java.format.settings.profile": "Eclipse [built-in]",
"editor.formatOnSave": true
"editor.formatOnSave": true,
"[java]": {
"editor.defaultFormatter": "redhat.java",
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"editor.tabSize": 4
}
}
37 changes: 37 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,40 @@ So a user is always the pair, stored and passed together, never the id alone:

New code always keeps the pair. When a change works with a table, method or API that has only
the user id, it adds the type beside it.

## A reactor describes itself in its definition

A reactor tells its MCP tool, its help text and the SDKs what it does by overriding
`getReactorDefinition()` (`prerna.reactor.ReactorDefinition`). It calls
`super.getReactorDefinition()`, which starts from `keysToGet` and `keyRequired`, and adds
its description, its keys' descriptions and types, and its output:

```java
@Override
public ReactorDefinition getReactorDefinition() {
return super.getReactorDefinition()
.description("Stop the current execution of a pixel job")
.key(ReactorKeysEnum.ID, "The id for the job")
.key(PATHS_KEY, key -> key.list(String.class).description("The file paths"))
.output(String.class, "Whether the job was stopped");
}
```

- **Metadata only.** `keysToGet` and `keyRequired` still decide how a call's inputs are read.
The definition can say more, such as `key.required(true)`, without changing that.
- **No keys on purpose.** A reactor that takes no input says `.noKeys()`.
- **Admin reactors say so first.** A reactor only admins can run starts its description with
`Admin Only: `, such as `"Admin Only: Returns every engine on the server, ..."`. A reactor that
admins can run with extra access, but other users can run too, does not.
- **Parents describe only shared keys.** A parent's definition describes only the keys every
reactor below it takes. Text several reactors share lives in the parent as a constant, such as
`TEAM_ID_DESCRIPTION`, and each reactor describes the keys it takes with it. A reactor that does
not take its parent's keys starts from `startDefinition()` instead of
`super.getReactorDefinition()`. Describing a key a reactor does not take, or listing a key twice
in `keysToGet`, is reported, with the class to fix, and fails the OpenAPI export.
- **Reading it.** Code that needs a reactor's description or a key's type calls
`getReactorDefinition()`, not `getReactorDescription`, `getDescriptionForKey` or
`getKeyTypeForMCP`.
- **Migrating.** Those three methods are deprecated. New code does not override them, and a
change that touches a reactor still overriding them moves the values into
`getReactorDefinition()`, as each method's javadoc shows, and deletes the old methods.
9 changes: 9 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
cannot share a single property. -->
<jackson2.version>2.22.4</jackson2.version>
<jackson2.annotations.version>2.22</jackson2.annotations.version>
<!-- OpenAPI annotations for reactor contracts (AbstractReactor2); keep in step with Monolith's swagger.version -->
<swagger.version>2.2.30</swagger.version>
<guava.version>33.7.2-jre</guava.version>
<jackson3.version>3.1.8</jackson3.version>
<!-- Keep these security pins in sync with Monolith/pom.xml. -->
Expand Down Expand Up @@ -1989,6 +1991,13 @@
<artifactId>jackson-annotations</artifactId>
<version>${jackson2.annotations.version}</version>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/io.swagger.core.v3/swagger-annotations-jakarta -->
<!-- Annotations only: describes AbstractReactor2 inputs for the OpenAPI document Monolith exports -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
Expand Down
72 changes: 34 additions & 38 deletions src/prerna/auth/utils/reactors/admin/AdminEngineInfoReactor.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,85 +36,81 @@
import prerna.auth.utils.SecurityEngineUtils;
import prerna.auth.utils.SecurityQueryUtils;
import prerna.reactor.AbstractReactor;
import prerna.reactor.ReactorDefinition;
import prerna.sablecc2.om.GenRowStruct;
import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.PixelOperationType;
import prerna.sablecc2.om.ReactorKeysEnum;
import prerna.sablecc2.om.nounmeta.NounMetadata;

public class AdminEngineInfoReactor extends AbstractReactor {

public AdminEngineInfoReactor() {
this.keysToGet = new String[]{ReactorKeysEnum.ENGINE.getKey(), ReactorKeysEnum.META_KEYS.getKey()};
this.keysToGet = new String[] { ReactorKeysEnum.ENGINE.getKey(), ReactorKeysEnum.META_KEYS.getKey() };
}

@Override
public NounMetadata execute() {
User user = this.insight.getUser();
SecurityAdminUtils adminUtils = SecurityAdminUtils.getInstance(user);
if(adminUtils == null) {
if (adminUtils == null) {
throw new IllegalArgumentException("User must be an admin to perform this function");
}
organizeKeys();
String engineId = this.keyValue.get(this.keysToGet[0]);
if(engineId == null || engineId.isEmpty()) {

if (engineId == null || engineId.isEmpty()) {
throw new IllegalArgumentException("Must input an engine id");
}
engineId = SecurityQueryUtils.testUserEngineIdForAlias(this.insight.getUser(), engineId);

List<Map<String, Object>> baseInfo = adminUtils.getAllEngineSettings(Arrays.asList(engineId), null, null, null, null, null);
if(baseInfo == null || baseInfo.isEmpty()) {
List<Map<String, Object>> baseInfo = adminUtils.getAllEngineSettings(Arrays.asList(engineId), null, null, null,
null, null);
if (baseInfo == null || baseInfo.isEmpty()) {
throw new IllegalArgumentException("Could not find any engine data");
}

// we filtered to a single database
Map<String, Object> databaseInfo = baseInfo.get(0);
databaseInfo.putAll(SecurityEngineUtils.getAggregateEngineMetadata(engineId, getMetaKeys(), true));
return new NounMetadata(databaseInfo, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.ENGINE_INFO);
}

private List<String> getMetaKeys() {
GenRowStruct grs = this.store.getGenRowStruct(ReactorKeysEnum.META_KEYS.getKey());
if(grs != null && !grs.isEmpty()) {
if (grs != null && !grs.isEmpty()) {
return grs.getAllStrValues();
}

return null;
}

@Override
public String getReactorDescription() {
return """
Admin-only: returns the full settings/metadata record for a single engine regardless of the caller's \
engine permissions. The calling user must be an application admin or the reactor throws \
"User must be an admin to perform this function".
public ReactorDefinition getReactorDefinition() {
return super.getReactorDefinition()
.description(
"""
Admin Only: Returns the full settings/metadata record for a single engine regardless of the caller's \
engine permissions. The calling user must be an application admin or the reactor throws \
"User must be an admin to perform this function".

This is the admin counterpart to EngineInfo, which instead enforces the caller's view/discoverable access.
This is the admin counterpart to EngineInfo, which instead enforces the caller's view/discoverable access.

Inputs:
engine (required) - the engine id.
metaKeys (optional) - restrict the returned metadata tags to this list; omit to return all metadata.
Inputs:
engine (required) - the engine id.
metaKeys (optional) - restrict the returned metadata tags to this list; omit to return all metadata.

Returns a single map (ENGINE_INFO/CUSTOM_DATA_STRUCTURE) containing:
Core engine fields: engine_id, engine_name, engine_display_name, engine_type, engine_subtype,
engine_cost, engine_discoverable, engine_global, engine_tool_app, engine_created_by,
engine_created_by_type, engine_date_created, low_engine_name.
Legacy aliases (deprecated, kept for backwards compatibility - prefer the engine_* fields above):
database_id, database_name, database_tool_app, database_global, low_database_name,
app_id, app_name, app_display_name, app_global, tool_app.
Metadata: one entry per metadata tag (e.g. tag, domain, etc.); a tag with multiple values is returned as a list.
""";
}

@Override
protected String getDescriptionForKey(String key) {
if (ReactorKeysEnum.ENGINE.getKey().equals(key)) {
return "Id of the engine to look up";
} else if (ReactorKeysEnum.META_KEYS.getKey().equals(key)) {
return "Optional list of metadata tag names to return for the engine; omit to return all metadata tags";
}
return super.getDescriptionForKey(key);
Returns a single map (ENGINE_INFO/CUSTOM_DATA_STRUCTURE) containing:
Core engine fields: engine_id, engine_name, engine_display_name, engine_type, engine_subtype,
engine_cost, engine_discoverable, engine_global, engine_tool_app, engine_created_by,
engine_created_by_type, engine_date_created, low_engine_name.
Legacy aliases (deprecated, kept for backwards compatibility - prefer the engine_* fields above):
database_id, database_name, database_tool_app, database_global, low_database_name,
app_id, app_name, app_display_name, app_global, tool_app.
Metadata: one entry per metadata tag (e.g. tag, domain, etc.); a tag with multiple values is returned as a list.
""")
.key(ReactorKeysEnum.ENGINE, "Id of the engine to look up").key(ReactorKeysEnum.META_KEYS,
"Optional list of metadata tag names to return for the engine; omit to return all metadata tags");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
import prerna.logging.SemossLogUtils;
import prerna.om.InsightFile;
import prerna.reactor.AbstractReactor;
import prerna.reactor.ReactorDefinition;
import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.PixelOperationType;
import prerna.sablecc2.om.nounmeta.NounMetadata;
Expand Down Expand Up @@ -764,7 +765,27 @@ private static String title(String value) {
}

@Override
public String getReactorDescription() {
return "Exports Admin-Only Enterprise Usage Reports As Audited CSV Or PDF Downloads.";
public ReactorDefinition getReactorDefinition() {
return super.getReactorDefinition()
.description("Admin Only: Exports enterprise usage reports as audited CSV or PDF downloads.")
.key("app",
"Filters to apps whose id or name contains this text, or to the exact app id given after a leading =. Audit logs match the app id only.")
.key("comparisonEndDate",
"Last day, as YYYY-MM-DD, of the benchmark period the overview view compares against. When neither comparison date is given, the overview compares against the same number of days just before startDate.")
.key("comparisonStartDate",
"First day, as YYYY-MM-DD, of the benchmark period the overview view compares against. When neither comparison date is given, the overview compares against the same number of days just before startDate.")
.key("dimension",
"What the ranking view groups model usage by: user, app or model. The ranking export lists the top 20.")
.key("engine",
"Filters to engines whose id or name contains this text, or to the exact engine id given after a leading =.")
.key("format",
"The file to export: csv, the default, or pdf. A pdf export supports the overview view only.")
.key("model", "The engine filter, used when engine is not given. It matches the same way engine does.")
.key("source",
"Which logs to report on: model, the default, activity or audit. An audit export supports the logs view only, and the ranking view needs the model source.")
.key("user",
"Filters to users whose id or name contains this text, or to the exact user id given after a leading =. Audit logs match the acting or the affected user.")
.key("view",
"The report to export: overview, the default, ranking or logs. The logs view holds up to the newest 5000 rows.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import prerna.auth.utils.EnterpriseUsageUtils;
import prerna.auth.utils.SecurityAdminUtils;
import prerna.reactor.AbstractReactor;
import prerna.reactor.ReactorDefinition;
import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.nounmeta.NounMetadata;

Expand All @@ -54,7 +55,11 @@ public NounMetadata execute() {
}

@Override
public String getReactorDescription() {
return "Administrator-only retrieval of retained activity content, a model message pair, or an audit event's old and new values, or why its request was denied, by recordId. Pairing and identity boundaries are resolved on the server.";
public ReactorDefinition getReactorDefinition() {
return super.getReactorDefinition().description(
"Admin Only: Retrieval of retained activity content, a model message pair, or an audit event's old and new values, or why its request was denied, by recordId. Pairing and identity boundaries are resolved on the server.")
.key("recordId",
"The id of the record to read: a message id for the model source, a log id for the activity source, or an event id for the audit source.")
.key("source", "Where the record is kept: model, activity or audit.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import prerna.auth.utils.EnterpriseUsageUtils;
import prerna.auth.utils.SecurityAdminUtils;
import prerna.reactor.AbstractReactor;
import prerna.reactor.ReactorDefinition;
import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.nounmeta.NounMetadata;

Expand All @@ -55,7 +56,11 @@ public NounMetadata execute() {
}

@Override
public String getReactorDescription() {
return "Lists Registered User IDs, Names And Authentication Types, App IDs And Names, Or Engine IDs And Names For Enterprise Usage Filters. Supports Bound Search, Exact ID Resolution And Bounded Pagination.";
public ReactorDefinition getReactorDefinition() {
return super.getReactorDefinition().description(
"Admin Only: Lists registered user IDs, names and authentication types, app IDs and names, or engine IDs and names for enterprise usage filters. Supports bound search, exact ID resolution and bounded pagination.")
.key("dimension", "Which catalog to list: user, app or engine.")
.key("id", "An exact id to look up in the catalog. When it is given, search is not used.")
.key("limit", key -> key.type(Integer.class)).key("offset", key -> key.type(Integer.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import prerna.auth.utils.EnterpriseUsageUtils;
import prerna.auth.utils.SecurityAdminUtils;
import prerna.reactor.AbstractReactor;
import prerna.reactor.ReactorDefinition;
import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.nounmeta.NounMetadata;

Expand Down Expand Up @@ -59,7 +60,22 @@ public NounMetadata execute() {
}

@Override
public String getReactorDescription() {
return "Administrator-only enterprise usage summary, daily trends, latency, feedback, rankings, or paginated metadata from model/activity logs, or a summary and paginated metadata of the audit trail's access changes, denied requests and usage limit changes. Model summaries, trends and rankings include their cost in millionths of a dollar, recorded and estimated; model log rows include their token parts, cost and whom the call was billed to. Dates are inclusive; filters match name/ID fragments or =exact IDs. No SQL input is accepted.";
public ReactorDefinition getReactorDefinition() {
return super.getReactorDefinition().description(
"Admin Only: Enterprise usage summary, daily trends, latency, feedback, rankings, or paginated metadata from model/activity logs, or a summary and paginated metadata of the audit trail's access changes, denied requests and usage limit changes. Model summaries, trends and rankings include their cost in millionths of a dollar, recorded and estimated; model log rows include their token parts, cost and whom the call was billed to. Dates are inclusive; filters match name/ID fragments or =exact IDs. No SQL input is accepted.")
.key("app",
"Filters to apps whose id or name contains this text, or to the exact app id given after a leading =. Audit logs match the app id only.")
.key("dimension",
"What the ranking view groups model usage by: user, app or model. Used by the ranking view only.")
.key("engine",
"Filters to engines whose id or name contains this text, or to the exact engine id given after a leading =.")
.key("limit", key -> key.type(Integer.class))
.key("model", "The engine filter, used when engine is not given. It matches the same way engine does.")
.key("offset", key -> key.type(Integer.class))
.key("source", "Which logs to read: model, activity or audit.")
.key("user",
"Filters to users whose id or name contains this text, or to the exact user id given after a leading =. Audit logs match the acting or the affected user.")
.key("view",
"The report to return: summary, trend, latency, feedback, ranking or logs. The activity source supports summary, trend and logs, and the audit source supports summary and logs.");
}
}
Loading
Loading