diff --git a/opencti-platform/opencti-front/src/schema/relay.schema.graphql b/opencti-platform/opencti-front/src/schema/relay.schema.graphql index ba58c66230f5..b4d0085494ef 100644 --- a/opencti-platform/opencti-front/src/schema/relay.schema.graphql +++ b/opencti-platform/opencti-front/src/schema/relay.schema.graphql @@ -7072,6 +7072,7 @@ type Query { stixCoreObjectsExportFiles(first: Int, type: String!): FileConnection stixCoreObjectsTimeSeries(authorId: String, field: String!, operation: StatsOperation!, startDate: DateTime!, endDate: DateTime, interval: String!, onlyInferred: Boolean, types: [String], filters: FilterGroup, search: String, relationship_type: [String], elementId: [String]): [TimeSeries] stixCoreObjectsMultiTimeSeries(operation: StatsOperation!, startDate: DateTime!, endDate: DateTime, interval: String!, onlyInferred: Boolean, timeSeriesParameters: [StixCoreObjectsTimeSeriesParameters]): [MultiTimeSeries] + publicStixCoreObjectsMultiTimeSeries(startDate: DateTime!, endDate: DateTime, interval: String!, onlyInferred: Boolean, dashboardId: String!, widgetId: String!): [MultiTimeSeries] stixCoreObjectsNumber(types: [String], startDate: DateTime, endDate: DateTime, onlyInferred: Boolean, filters: FilterGroup, search: String, relationship_type: [String], elementId: [String]): Number stixCoreObjectsMultiNumber(startDate: DateTime, endDate: DateTime, onlyInferred: Boolean, numberParameters: [StixCoreObjectsNumberParameters]): [Number] stixCoreObjectsDistribution(objectId: [String], relationship_type: [String], toTypes: [String], elementWithTargetTypes: [String], field: String!, startDate: DateTime, endDate: DateTime, dateAttribute: String, operation: StatsOperation!, limit: Int, order: String, types: [String], filters: FilterGroup, search: String): [Distribution] diff --git a/opencti-platform/opencti-graphql/config/schema/opencti.graphql b/opencti-platform/opencti-graphql/config/schema/opencti.graphql index 1e59c3fe41d6..378b37b9fd24 100644 --- a/opencti-platform/opencti-graphql/config/schema/opencti.graphql +++ b/opencti-platform/opencti-graphql/config/schema/opencti.graphql @@ -10938,6 +10938,14 @@ type Query { onlyInferred: Boolean timeSeriesParameters: [StixCoreObjectsTimeSeriesParameters] ): [MultiTimeSeries] @auth(for: [KNOWLEDGE, EXPLORE]) + publicStixCoreObjectsMultiTimeSeries( + startDate: DateTime! + endDate: DateTime + interval: String! + onlyInferred: Boolean + dashboardId: String! + widgetId: String! + ): [MultiTimeSeries] stixCoreObjectsNumber( types: [String] startDate: DateTime diff --git a/opencti-platform/opencti-graphql/src/domain/stixCoreObject.js b/opencti-platform/opencti-graphql/src/domain/stixCoreObject.js index a68f02e6d22d..b1da6d7bad58 100644 --- a/opencti-platform/opencti-graphql/src/domain/stixCoreObject.js +++ b/opencti-platform/opencti-graphql/src/domain/stixCoreObject.js @@ -13,7 +13,7 @@ import { import { internalLoadById, listEntities, storeLoadById, storeLoadByIds } from '../database/middleware-loader'; import { findAll as relationFindAll } from './stixCoreRelationship'; import { delEditContext, lockResource, notify, setEditContext, storeUpdateEvent } from '../database/redis'; -import { BUS_TOPICS } from '../config/conf'; +import { BUS_TOPICS, logApp } from '../config/conf'; import { FunctionalError, LockTimeoutError, TYPE_LOCK_ERROR, UnsupportedError } from '../config/errors'; import { isStixCoreObject, stixCoreObjectOptions } from '../schema/stixCoreObject'; import { findById as findStatusById } from './status'; @@ -56,7 +56,7 @@ import { findById as documentFindById } from '../modules/internal/document/docum import { elCount, elUpdateElement } from '../database/engine'; import { generateStandardId, getInstanceIds } from '../schema/identifier'; import { askEntityExport, askListExport, exportTransformFilters } from './stix'; -import { isEmptyField, isNotEmptyField, READ_ENTITIES_INDICES, READ_INDEX_INFERRED_ENTITIES } from '../database/utils'; +import { fromBase64, isEmptyField, isNotEmptyField, READ_ENTITIES_INDICES, READ_INDEX_INFERRED_ENTITIES } from '../database/utils'; import { RELATION_RELATED_TO, STIX_CORE_RELATIONSHIPS } from '../schema/stixCoreRelationship'; import { ENTITY_TYPE_CONTAINER_CASE } from '../modules/case/case-types'; import { getEntitySettingFromCache } from '../modules/entitySetting/entitySetting-utils'; @@ -66,6 +66,8 @@ import { extractEntityRepresentativeName } from '../database/entity-representati import { addFilter, extractFilterGroupValues } from '../utils/filtering/filtering-utils'; import { specialFilterKeysWhoseValueToResolve } from '../utils/filtering/filtering-constants'; import { schemaRelationsRefDefinition } from '../schema/schema-relationsRef'; +import { ENTITY_TYPE_WORKSPACE } from '../modules/workspace/workspace-types'; +import { findById as findUserById } from './user'; export const findAll = async (context, user, args) => { let types = []; @@ -308,6 +310,43 @@ export const stixCoreObjectsMultiTimeSeries = (context, user, args) => { })); }; +export const publicStixCoreObjectsMultiTimeSeries = async (context, user, args) => { + // TODO : get User and Dashboard from cache + // TODO Get User from manifest => user should be added to manifest + + // Get dashboard manifest + const dashboard = await storeLoadById( + context, + user, + args.dashboardId, + ENTITY_TYPE_WORKSPACE, + ); + + // Get widget query configuration + const parsedManifest = JSON.parse(fromBase64(dashboard.manifest) ?? '{}'); + const { widgets } = parsedManifest; + const widgetConfigs = widgets[args.widgetId].dataSelection; + const timeSeriesParameters = []; + widgetConfigs.map((widgetConfig) => timeSeriesParameters.push({ + field: widgetConfig.date_attribute, + filters: widgetConfig.filters, + types: [ + 'Stix-Core-Object' // ?? should be in the manifest + ] + })); + + const standardArgs = { + operation: 'count', // TODO 'count' is harcoded for now but should come from manifest + startDate: args.startDate, + endDate: args.endDate, + interval: args.interval, + timeSeriesParameters + }; + + // Use standard API + return stixCoreObjectsMultiTimeSeries(context, user, standardArgs); +}; + export const stixCoreObjectsNumber = (context, user, args) => { let types = []; if (isNotEmptyField(args.types)) { diff --git a/opencti-platform/opencti-graphql/src/generated/graphql.ts b/opencti-platform/opencti-graphql/src/generated/graphql.ts index b5d8189cae2b..5843043f0b96 100644 --- a/opencti-platform/opencti-graphql/src/generated/graphql.ts +++ b/opencti-platform/opencti-graphql/src/generated/graphql.ts @@ -17076,6 +17076,7 @@ export type Query = { playbooks?: Maybe; position?: Maybe; positions?: Maybe; + publicStixCoreObjectsMultiTimeSeries?: Maybe>>; rabbitMQMetrics?: Maybe; region?: Maybe; regions?: Maybe; @@ -18371,6 +18372,16 @@ export type QueryPositionsArgs = { }; +export type QueryPublicStixCoreObjectsMultiTimeSeriesArgs = { + dashboardId: Scalars['String']['input']; + endDate?: InputMaybe; + interval: Scalars['String']['input']; + onlyInferred?: InputMaybe; + startDate: Scalars['DateTime']['input']; + widgetId: Scalars['String']['input']; +}; + + export type QueryRabbitMqMetricsArgs = { prefix?: InputMaybe; }; @@ -33961,6 +33972,7 @@ export type QueryResolvers, ParentType, ContextType, Partial>; position?: Resolver, ParentType, ContextType, RequireFields>; positions?: Resolver, ParentType, ContextType, Partial>; + publicStixCoreObjectsMultiTimeSeries?: Resolver>>, ParentType, ContextType, RequireFields>; rabbitMQMetrics?: Resolver, ParentType, ContextType, Partial>; region?: Resolver, ParentType, ContextType, RequireFields>; regions?: Resolver, ParentType, ContextType, Partial>; diff --git a/opencti-platform/opencti-graphql/src/resolvers/stixCoreObject.js b/opencti-platform/opencti-graphql/src/resolvers/stixCoreObject.js index bc8fa7f7abf5..442fdb487465 100644 --- a/opencti-platform/opencti-graphql/src/resolvers/stixCoreObject.js +++ b/opencti-platform/opencti-graphql/src/resolvers/stixCoreObject.js @@ -14,6 +14,7 @@ import { findAll, findById, findFiltersRepresentatives, + publicStixCoreObjectsMultiTimeSeries, stixCoreObjectAddRelation, stixCoreObjectAddRelations, stixCoreObjectCleanContext, @@ -34,7 +35,7 @@ import { stixCoreObjectsNumber, stixCoreObjectsTimeSeries, stixCoreObjectsTimeSeriesByAuthor, - stixCoreRelationships, + stixCoreRelationships } from '../domain/stixCoreObject'; import { fetchEditContext, pubSubAsyncIterator } from '../database/redis'; import { batchLoader, distributionRelations, stixLoadByIdStringify } from '../database/middleware'; @@ -74,6 +75,7 @@ const stixCoreObjectResolvers = { return stixCoreObjectsTimeSeries(context, context.user, args); }, stixCoreObjectsMultiTimeSeries: (_, args, context) => stixCoreObjectsMultiTimeSeries(context, context.user, args), + publicStixCoreObjectsMultiTimeSeries: (_, args, context) => publicStixCoreObjectsMultiTimeSeries(context, context.user, args), stixCoreObjectsNumber: (_, args, context) => stixCoreObjectsNumber(context, context.user, args), stixCoreObjectsMultiNumber: (_, args, context) => stixCoreObjectsMultiNumber(context, context.user, args), stixCoreObjectsDistribution: (_, args, context) => {