Skip to content

Commit

Permalink
Rewrote the interfaces of the Splunk object to follow the hierarchy o…
Browse files Browse the repository at this point in the history
…f the Javascript SplunkSDK

Will be used to simplify the creation of a wrapper to generate Promises on top of the JS SDK
  • Loading branch information
Marc-Antoine Hinse committed Jan 16, 2025
1 parent 5728ccd commit e9e720f
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 172 deletions.
8 changes: 4 additions & 4 deletions packages/react-components/src/StatusScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { FC, useEffect, useState } from 'react';
import Button from './components/Button';
import './global.css';
import { SplunkCollectionItem } from './models/splunk';
import { KVCollectionItem } from './models/splunk';
import './StatusScreen.css';
import {
fetchCollectionItems,
Expand Down Expand Up @@ -37,7 +37,7 @@ const StatusScreen: FC<{ theme: string }> = ({ theme }) => {
fetchCollectionItems(),
fetchVersionName('unknown'),
fetchCurrentIndexName(),
]).then(([id, splunkCollectionItems, version, indexName]) => {
]).then(([id, kvCollectionItems, version, indexName]) => {
const items: StatusItem[] = [
{
key: StatusItemKeys.VERSION,
Expand All @@ -57,7 +57,7 @@ const StatusScreen: FC<{ theme: string }> = ({ theme }) => {
value: `${id}`,
},
];
splunkCollectionItems.forEach((item) => {
kvCollectionItems.forEach((item) => {
if (item.key === StatusItemKeys.LAST_FETCHED) {
items.push({
key: item.key.startsWith(CollectionKeys.NEXT_PREFIX)
Expand Down Expand Up @@ -118,7 +118,7 @@ const StatusScreen: FC<{ theme: string }> = ({ theme }) => {
return 'Unknown Status Item';
}

function formatValue(item: SplunkCollectionItem): string {
function formatValue(item: KVCollectionItem): string {
if (item.key === StatusItemKeys.START_DATE || item.key === StatusItemKeys.LAST_FETCHED) {
const date = new Date(item.value);
return date.toLocaleString();
Expand Down
4 changes: 2 additions & 2 deletions packages/react-components/src/models/constants.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SplunkApplicationNamespace } from './splunk';
import { ApplicationNamespace } from './splunk';

export const APP_NAME = 'flare';
export const STORAGE_REALM = 'flare_integration_realm';
export const APPLICATION_NAMESPACE: SplunkApplicationNamespace = {
export const APPLICATION_NAMESPACE: ApplicationNamespace = {
owner: 'nobody',
app: APP_NAME,
sharing: 'app',
Expand Down
115 changes: 39 additions & 76 deletions packages/react-components/src/models/splunk.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,66 @@
export interface SplunkApplicationNamespace {
app?: string;
owner?: string;
sharing?: string;
}

export interface SplunkPassword {
name: string;
_properties: {
clear_password: string;
};
}

export interface SplunkCollectionItem {
export interface KVCollectionItem {
key: string;
value: string;
user: string;
}

export interface SplunkIndex {
name: string;
}

export interface SplunkSavedSearch {
name: string;
qualifiedPath: string;
update: (properties: Record<string, string>) => SplunkRequestResponse;
}

export interface SplunkAppAccessor {
reload: () => void;
}

export interface Stanza {
name: string;
export interface ApplicationNamespace {
app?: string;
owner?: string;
sharing?: string;
}

export interface SplunkRequestResponse {
export interface HTTPResponse {
status: number;
data: any;
}

export interface SplunkAppsAccessor {
fetch: () => SplunkAppsAccessor;
item: (applicationName: string) => SplunkAppAccessor;
export interface Endpoint {
fetch: () => this;
del: (relativePath: string) => HTTPResponse;
qualifiedPath: string;
}

export interface ConfigurationStanzaAccessor {
fetch: () => ConfigurationStanzaAccessor;
update: (properties: Record<string, string>) => SplunkRequestResponse;
list: () => Array<{ name: string }>;
export interface Resource extends Endpoint {
fetch: () => this;
properties: () => Record<string, string>;
_properties: Record<string, string>;
}

export interface ConfigurationFileAccessor {
create: (stanzaName: string) => SplunkRequestResponse;
fetch: () => ConfigurationFileAccessor;
item: (
stanzaName: string,
properties: SplunkApplicationNamespace
) => ConfigurationStanzaAccessor;
list: () => Array<{ name: string }>;
export interface Entity extends Resource {
reload: () => void;
update: (properties: Record<string, string>) => HTTPResponse;
name: string;
}

export interface ConfigurationsAccessor {
fetch: () => ConfigurationsAccessor;
create: (configurationFilename: string) => SplunkRequestResponse;
item: (stanzaName: string, properties: SplunkApplicationNamespace) => ConfigurationFileAccessor;
list: () => Array<{ name: string }>;
export interface Collection<T extends Entity | Collection<Entity>> extends Resource {
item: (itemName: string, namespace: ApplicationNamespace) => T;
list: () => Array<T>;
}

export interface ConfigurationFile extends Collection<Entity> {
create: (stanzaName: string) => HTTPResponse;
name: string;
}

export interface SplunkIndexesAccessor {
fetch: () => SplunkIndexesAccessor;
create: (indexName: string, data: any) => SplunkRequestResponse;
item: (indexName: string) => SplunkIndex;
list: () => Array<SplunkIndex>;
export interface Configurations extends Collection<ConfigurationFile> {
create: (configurationFilename: string) => HTTPResponse;
}

export interface SplunkSavedSearchAccessor {
fetch: () => SplunkSavedSearchAccessor;
create: (indexName: string, data: any) => SplunkRequestResponse;
item: (indexName: string) => SplunkSavedSearch;
list: () => Array<SplunkSavedSearch>;
export interface Indexes extends Collection<Entity> {
create: (indexName: string, data: any) => HTTPResponse;
}

export interface SplunkStoragePasswordAccessors {
fetch: () => SplunkStoragePasswordAccessors;
item: (applicationName: string) => SplunkAppAccessor;
list: () => Array<SplunkPassword>;
del: (passwordId: string) => SplunkRequestResponse;
create: (params: { name: string; realm: string; password: string }) => SplunkRequestResponse;
export interface StoragePasswords extends Collection<Entity> {
create: (params: { name: string; realm: string; password: string }) => HTTPResponse;
}

export interface SplunkService {
configurations: (params: SplunkApplicationNamespace) => ConfigurationsAccessor;
apps: () => SplunkAppsAccessor;
storagePasswords: () => SplunkStoragePasswordAccessors;
indexes: () => SplunkIndexesAccessor;
savedSearches: () => SplunkSavedSearchAccessor;
get: (splunkUrlPath: string, data: any) => SplunkRequestResponse;
post: (splunkUrlPath: string, data: any) => SplunkRequestResponse;
export interface Service {
configurations: (params: ApplicationNamespace) => Configurations;
apps: () => Collection<Entity>;
storagePasswords: () => StoragePasswords;
indexes: () => Indexes;
savedSearches: () => Collection<Entity>;
serverInfo: () => any;
get: (splunkUrlPath: string, data: any) => HTTPResponse;
post: (splunkUrlPath: string, data: any) => HTTPResponse;
}
98 changes: 39 additions & 59 deletions packages/react-components/src/utils/configurationFileHelper.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import {
ConfigurationFileAccessor,
ConfigurationsAccessor,
ConfigurationStanzaAccessor,
SplunkService,
} from '../models/splunk';
import { APPLICATION_NAMESPACE } from '../models/constants';
import { ConfigurationFile, Configurations, Entity, HTTPResponse, Service } from '../models/splunk';
import { promisify } from './util';

// ----------------------------------
Expand All @@ -13,10 +9,10 @@ import { promisify } from './util';
// Existence Functions
// ---------------------
function doesConfigurationExist(
configurationsAccessor: ConfigurationsAccessor,
configurations: Configurations,
configurationFilename: string
): boolean {
for (const stanza of configurationsAccessor.list()) {
for (const stanza of configurations.list()) {
if (stanza.name === configurationFilename) {
return true;
}
Expand All @@ -26,7 +22,7 @@ function doesConfigurationExist(
}

function doesStanzaExist(
configurationFileAccessor: ConfigurationFileAccessor,
configurationFileAccessor: ConfigurationFile,
stanzaName: string
): boolean {
for (const stanza of configurationFileAccessor.list()) {
Expand All @@ -42,62 +38,54 @@ function doesStanzaExist(
// Retrieval Functions
// ---------------------
function getConfigurationFile(
configurationsAccessor: ConfigurationsAccessor,
configurations: Configurations,
configurationFilename: string
): ConfigurationFileAccessor {
const configurationFileAccessor = configurationsAccessor.item(configurationFilename, {
// Name space information not provided
});

return configurationFileAccessor;
): Promise<ConfigurationFile> {
return promisify(configurations.item(configurationFilename, APPLICATION_NAMESPACE).fetch)();
}

function getConfigurationFileStanza(
configurationFileAccessor: ConfigurationFileAccessor,
configurationFile: ConfigurationFile,
configurationStanzaName: string
): ConfigurationStanzaAccessor {
const configurationStanzaAccessor = configurationFileAccessor.item(configurationStanzaName, {
// Name space information not provided
});

return configurationStanzaAccessor;
): Promise<Entity> {
return promisify(
configurationFile.item(configurationStanzaName, APPLICATION_NAMESPACE).fetch
)();
}

function createStanza(
configurationFileAccessor: ConfigurationFileAccessor,
configurationFile: ConfigurationFile,
newStanzaName: string
): Promise<void> {
return promisify(configurationFileAccessor.create)(newStanzaName);
): Promise<HTTPResponse> {
return promisify(configurationFile.create)(newStanzaName);
}

function updateStanzaProperties(
configurationStanzaAccessor: ConfigurationStanzaAccessor,
configurationStanza: Entity,
newStanzaProperties: Record<string, string>
): Promise<void> {
return promisify(configurationStanzaAccessor.update)(newStanzaProperties);
): Promise<HTTPResponse> {
return promisify(configurationStanza.update)(newStanzaProperties);
}

// ---------------------
// Process Helpers
// ---------------------

function createConfigurationFile(
configurationsAccessor: ConfigurationsAccessor,
configurations: Configurations,
configurationFilename: string
): Promise<void> {
return promisify(configurationsAccessor.create)(configurationFilename);
): Promise<HTTPResponse> {
return promisify(configurations.create)(configurationFilename);
}

export async function updateConfigurationFile(
service: SplunkService,
service: Service,
configurationFilename: string,
stanzaName: string,
properties: Record<string, string>
): Promise<void> {
// Fetch the accessor used to get a configuration file
let configurations = service.configurations({
// Name space information not provided
});
let configurations = service.configurations(APPLICATION_NAMESPACE);
configurations = await promisify(configurations.fetch)();

// Check for the existence of the configuration file
Expand All @@ -111,61 +99,53 @@ export async function updateConfigurationFile(
}

// Fetchs the configuration file accessor
let configurationFileAccessor = getConfigurationFile(configurations, configurationFilename);
configurationFileAccessor = await promisify(configurationFileAccessor.fetch)();
let configurationFile = await getConfigurationFile(configurations, configurationFilename);

// Checks to see if the stanza where the inputs will be
// stored exist
const stanzaExist = doesStanzaExist(configurationFileAccessor, stanzaName);
const stanzaExist = doesStanzaExist(configurationFile, stanzaName);

// If the configuration stanza doesn't exist, create it
if (!stanzaExist) {
await createStanza(configurationFileAccessor, stanzaName);
await createStanza(configurationFile, stanzaName);

// Need to update the information after the creation of the stanza
configurationFile = await promisify(configurationFile.fetch)();
}
// Need to update the information after the creation of the stanza
configurationFileAccessor = await promisify(configurationFileAccessor.fetch)();

// Fetchs the configuration stanza accessor
let configurationStanzaAccessor = getConfigurationFileStanza(
configurationFileAccessor,
stanzaName
);
configurationStanzaAccessor = await promisify(configurationStanzaAccessor.fetch)();
const configurationStanza = await getConfigurationFileStanza(configurationFile, stanzaName);

// We don't care if the stanza property does or doesn't exist
// This is because we can use the
// configurationStanza.update() function to create and
// change the information of a property
await updateStanzaProperties(configurationStanzaAccessor, properties);
await updateStanzaProperties(configurationStanza, properties);
}

export async function getConfigurationStanzaValue(
service: SplunkService,
service: Service,
configurationFilename: string,
stanzaName: string,
propertyName: string,
defaultValue: string
): Promise<string> {
// Fetch the accessor used to get a configuration file
let configurations = service.configurations({
// Name space information not provided
});
let configurations = service.configurations(APPLICATION_NAMESPACE);
configurations = await promisify(configurations.fetch)();

// Fetchs the configuration file accessor
let configurationFileAccessor = getConfigurationFile(configurations, configurationFilename);
configurationFileAccessor = await promisify(configurationFileAccessor.fetch)();
const configurationFile = await getConfigurationFile(configurations, configurationFilename);

// Fetchs the configuration stanza accessor
let configurationStanzaAccessor = getConfigurationFileStanza(
configurationFileAccessor,
const configurationStanzaAccessor = await getConfigurationFileStanza(
configurationFile,
stanzaName
);
configurationStanzaAccessor = await promisify(configurationStanzaAccessor.fetch)();

let propertyValue = defaultValue;
if (propertyName in configurationStanzaAccessor._properties) {
propertyValue = configurationStanzaAccessor._properties[propertyName];
if (propertyName in configurationStanzaAccessor.properties()) {
propertyValue = configurationStanzaAccessor.properties()[propertyName];
}

return propertyValue;
Expand Down
Loading

0 comments on commit e9e720f

Please sign in to comment.