1212import org .elasticsearch .search .sort .SortOrder ;
1313import org .elasticsearch .xpack .core .ml .action .GetBucketsAction ;
1414import org .elasticsearch .xpack .core .ml .action .GetRecordsAction ;
15+ import org .elasticsearch .xpack .core .ml .action .UpdateJobAction ;
1516import org .elasticsearch .xpack .core .ml .calendars .ScheduledEvent ;
1617import org .elasticsearch .xpack .core .ml .job .config .AnalysisConfig ;
1718import org .elasticsearch .xpack .core .ml .job .config .DataDescription ;
1819import org .elasticsearch .xpack .core .ml .job .config .Detector ;
1920import org .elasticsearch .xpack .core .ml .job .config .Job ;
21+ import org .elasticsearch .xpack .core .ml .job .config .JobUpdate ;
2022import org .elasticsearch .xpack .core .ml .job .results .AnomalyRecord ;
2123import org .elasticsearch .xpack .core .ml .job .results .Bucket ;
2224import org .junit .After ;
@@ -193,9 +195,9 @@ public void testScheduledEventWithInterimResults() throws IOException {
193195 /**
194196 * Test an open job picks up changes to scheduled events/calendars
195197 */
196- public void testOnlineUpdate () throws Exception {
198+ public void testAddEventsToOpenJob () throws Exception {
197199 TimeValue bucketSpan = TimeValue .timeValueMinutes (30 );
198- Job .Builder job = createJob ("scheduled-events-online-update " , bucketSpan );
200+ Job .Builder job = createJob ("scheduled-events-add-events-to-open-job " , bucketSpan );
199201
200202 long startTime = 1514764800000L ;
201203 final int bucketCount = 5 ;
@@ -209,7 +211,7 @@ public void testOnlineUpdate() throws Exception {
209211
210212 // Now create a calendar and events for the job while it is open
211213 String calendarId = "test-calendar-online-update" ;
212- putCalendar (calendarId , Collections .singletonList (job .getId ()), "testOnlineUpdate calendar" );
214+ putCalendar (calendarId , Collections .singletonList (job .getId ()), "testAddEventsToOpenJob calendar" );
213215
214216 List <ScheduledEvent > events = new ArrayList <>();
215217 long eventStartTime = startTime + (bucketCount + 1 ) * bucketSpan .millis ();
@@ -257,6 +259,81 @@ public void testOnlineUpdate() throws Exception {
257259 assertEquals (0 , buckets .get (8 ).getScheduledEvents ().size ());
258260 }
259261
262+ /**
263+ * An open job that later gets added to a calendar, should take the scheduled events into account
264+ */
265+ public void testAddOpenedJobToGroupWithCalendar () throws Exception {
266+ TimeValue bucketSpan = TimeValue .timeValueMinutes (30 );
267+ String groupName = "opened-calendar-job-group" ;
268+ Job .Builder job = createJob ("scheduled-events-add-opened-job-to-group-with-calendar" , bucketSpan );
269+
270+ long startTime = 1514764800000L ;
271+ final int bucketCount = 5 ;
272+
273+ // Open the job
274+ openJob (job .getId ());
275+
276+ // write some buckets of data
277+ postData (job .getId (), generateData (startTime , bucketSpan , bucketCount , bucketIndex -> randomIntBetween (100 , 200 ))
278+ .stream ().collect (Collectors .joining ()));
279+
280+ String calendarId = "test-calendar-open-job-update" ;
281+
282+ // Create a new calendar referencing groupName
283+ putCalendar (calendarId , Collections .singletonList (groupName ), "testAddOpenedJobToGroupWithCalendar calendar" );
284+
285+ // Put events in the calendar
286+ List <ScheduledEvent > events = new ArrayList <>();
287+ long eventStartTime = startTime + (bucketCount + 1 ) * bucketSpan .millis ();
288+ long eventEndTime = eventStartTime + (long )(1.5 * bucketSpan .millis ());
289+ events .add (new ScheduledEvent .Builder ().description ("Some Event" )
290+ .startTime (ZonedDateTime .ofInstant (Instant .ofEpochMilli (eventStartTime ), ZoneOffset .UTC ))
291+ .endTime (ZonedDateTime .ofInstant (Instant .ofEpochMilli (eventEndTime ), ZoneOffset .UTC ))
292+ .calendarId (calendarId ).build ());
293+
294+ postScheduledEvents (calendarId , events );
295+
296+ // Update the job to be a member of the group
297+ UpdateJobAction .Request jobUpdateRequest = new UpdateJobAction .Request (job .getId (),
298+ new JobUpdate .Builder (job .getId ()).setGroups (Collections .singletonList (groupName )).build ());
299+ client ().execute (UpdateJobAction .INSTANCE , jobUpdateRequest ).actionGet ();
300+
301+ // Wait until the notification that the job was updated is indexed
302+ assertBusy (() -> {
303+ SearchResponse searchResponse = client ().prepareSearch (".ml-notifications" )
304+ .setSize (1 )
305+ .addSort ("timestamp" , SortOrder .DESC )
306+ .setQuery (QueryBuilders .boolQuery ()
307+ .filter (QueryBuilders .termQuery ("job_id" , job .getId ()))
308+ .filter (QueryBuilders .termQuery ("level" , "info" ))
309+ ).get ();
310+ SearchHit [] hits = searchResponse .getHits ().getHits ();
311+ assertThat (hits .length , equalTo (1 ));
312+ assertThat (hits [0 ].getSourceAsMap ().get ("message" ), equalTo ("Job updated: [groups]" ));
313+ });
314+
315+ // write some more buckets of data that cover the scheduled event period
316+ postData (job .getId (), generateData (startTime + bucketCount * bucketSpan .millis (), bucketSpan , 5 ,
317+ bucketIndex -> randomIntBetween (100 , 200 ))
318+ .stream ().collect (Collectors .joining ()));
319+ // and close
320+ closeJob (job .getId ());
321+
322+ GetBucketsAction .Request getBucketsRequest = new GetBucketsAction .Request (job .getId ());
323+ List <Bucket > buckets = getBuckets (getBucketsRequest );
324+
325+ // the first 6 buckets have no events
326+ for (int i =0 ; i <=bucketCount ; i ++) {
327+ assertEquals (0 , buckets .get (i ).getScheduledEvents ().size ());
328+ }
329+ // 7th and 8th buckets have the event but the last one does not
330+ assertEquals (1 , buckets .get (6 ).getScheduledEvents ().size ());
331+ assertEquals ("Some Event" , buckets .get (6 ).getScheduledEvents ().get (0 ));
332+ assertEquals (1 , buckets .get (7 ).getScheduledEvents ().size ());
333+ assertEquals ("Some Event" , buckets .get (7 ).getScheduledEvents ().get (0 ));
334+ assertEquals (0 , buckets .get (8 ).getScheduledEvents ().size ());
335+ }
336+
260337 private Job .Builder createJob (String jobId , TimeValue bucketSpan ) {
261338 Detector .Builder detector = new Detector .Builder ("count" , null );
262339 AnalysisConfig .Builder analysisConfig = new AnalysisConfig .Builder (Collections .singletonList (detector .build ()));
0 commit comments