27
27
*/
28
28
package org .hisp .dhis .tracker .export .trackedentity ;
29
29
30
- import static org .hisp .dhis .common .OrganisationUnitSelectionMode .ALL ;
31
- import static org .hisp .dhis .common .OrganisationUnitSelectionMode .CHILDREN ;
32
- import static org .hisp .dhis .common .OrganisationUnitSelectionMode .DESCENDANTS ;
33
- import static org .hisp .dhis .common .OrganisationUnitSelectionMode .SELECTED ;
34
-
35
- import java .util .ArrayList ;
36
30
import java .util .HashSet ;
37
31
import java .util .LinkedHashSet ;
38
32
import java .util .List ;
48
42
import org .hisp .dhis .common .AccessLevel ;
49
43
import org .hisp .dhis .common .AuditType ;
50
44
import org .hisp .dhis .common .BaseIdentifiableObject ;
51
- import org .hisp .dhis .common .IllegalQueryException ;
52
45
import org .hisp .dhis .feedback .BadRequestException ;
53
46
import org .hisp .dhis .feedback .ForbiddenException ;
54
47
import org .hisp .dhis .feedback .NotFoundException ;
55
- import org .hisp .dhis .organisationunit .OrganisationUnit ;
56
- import org .hisp .dhis .organisationunit .OrganisationUnitService ;
57
48
import org .hisp .dhis .program .Enrollment ;
58
49
import org .hisp .dhis .program .Event ;
59
50
import org .hisp .dhis .program .Program ;
60
51
import org .hisp .dhis .program .ProgramService ;
61
52
import org .hisp .dhis .relationship .Relationship ;
62
53
import org .hisp .dhis .relationship .RelationshipItem ;
63
- import org .hisp .dhis .security .Authorities ;
64
- import org .hisp .dhis .security .acl .AclService ;
65
54
import org .hisp .dhis .trackedentity .TrackedEntity ;
66
55
import org .hisp .dhis .trackedentity .TrackedEntityAttribute ;
67
56
import org .hisp .dhis .trackedentity .TrackedEntityAttributeService ;
81
70
import org .hisp .dhis .tracker .export .trackedentity .aggregates .TrackedEntityAggregate ;
82
71
import org .hisp .dhis .user .CurrentUserService ;
83
72
import org .hisp .dhis .user .User ;
84
- import org .hisp .dhis .util .DateUtils ;
85
73
import org .springframework .stereotype .Service ;
86
74
import org .springframework .transaction .annotation .Transactional ;
87
75
@@ -99,16 +87,12 @@ class DefaultTrackedEntityService implements TrackedEntityService {
99
87
100
88
private final TrackedEntityAuditService trackedEntityAuditService ;
101
89
102
- private final OrganisationUnitService organisationUnitService ;
103
-
104
90
private final CurrentUserService currentUserService ;
105
91
106
92
private final TrackerAccessManager trackerAccessManager ;
107
93
108
94
private final TrackedEntityAggregate trackedEntityAggregate ;
109
95
110
- private final AclService aclService ;
111
-
112
96
private final ProgramService programService ;
113
97
114
98
private final EnrollmentService enrollmentService ;
@@ -343,298 +327,13 @@ public Page<TrackedEntity> getTrackedEntities(
343
327
}
344
328
345
329
public List <Long > getTrackedEntityIds (TrackedEntityQueryParams params ) {
346
- decideAccess (params );
347
- validate (params );
348
- validateSearchScope (params );
349
-
350
330
return trackedEntityStore .getTrackedEntityIds (params );
351
331
}
352
332
353
333
public Page <Long > getTrackedEntityIds (TrackedEntityQueryParams params , PageParams pageParams ) {
354
- decideAccess (params );
355
- validate (params );
356
- validateSearchScope (params );
357
-
358
334
return trackedEntityStore .getTrackedEntityIds (params , pageParams );
359
335
}
360
336
361
- public void decideAccess (TrackedEntityQueryParams params ) {
362
- if (params .isOrganisationUnitMode (ALL )
363
- && !currentUserService .currentUserIsAuthorized (
364
- Authorities .F_TRACKED_ENTITY_INSTANCE_SEARCH_IN_ALL_ORGUNITS .name ())) {
365
- throw new IllegalQueryException (
366
- "Current user is not authorized to query across all organisation units" );
367
- }
368
-
369
- User user = params .getUser ();
370
- if (params .hasProgram ()) {
371
- if (!aclService .canDataRead (user , params .getProgram ())) {
372
- throw new IllegalQueryException (
373
- "Current user is not authorized to read data from selected program: "
374
- + params .getProgram ().getUid ());
375
- }
376
-
377
- if (params .getProgram ().getTrackedEntityType () != null
378
- && !aclService .canDataRead (user , params .getProgram ().getTrackedEntityType ())) {
379
- throw new IllegalQueryException (
380
- "Current user is not authorized to read data from selected program's tracked entity type: "
381
- + params .getProgram ().getTrackedEntityType ().getUid ());
382
- }
383
- }
384
-
385
- if (params .hasTrackedEntityType ()
386
- && !aclService .canDataRead (user , params .getTrackedEntityType ())) {
387
- throw new IllegalQueryException (
388
- "Current user is not authorized to read data from selected tracked entity type: "
389
- + params .getTrackedEntityType ().getUid ());
390
- } else {
391
- params .setTrackedEntityTypes (
392
- trackedEntityTypeService .getAllTrackedEntityType ().stream ()
393
- .filter (tet -> aclService .canDataRead (user , tet ))
394
- .collect (Collectors .toList ()));
395
- }
396
- }
397
-
398
- public void validate (TrackedEntityQueryParams params ) throws IllegalQueryException {
399
- String violation = null ;
400
-
401
- if (params == null ) {
402
- throw new IllegalQueryException ("Params cannot be null" );
403
- }
404
-
405
- if (params .hasProgram () && params .hasTrackedEntityType ()) {
406
- violation = "Program and tracked entity cannot be specified simultaneously" ;
407
- }
408
-
409
- if (!params .hasTrackedEntities () && !params .hasProgram () && !params .hasTrackedEntityType ()) {
410
- violation = "Either Program or Tracked entity type should be specified" ;
411
- }
412
-
413
- if (params .hasProgramStatus () && !params .hasProgram ()) {
414
- violation = "Program must be defined when program status is defined" ;
415
- }
416
-
417
- if (params .hasFollowUp () && !params .hasProgram ()) {
418
- violation = "Program must be defined when follow up status is defined" ;
419
- }
420
-
421
- if (params .hasProgramEnrollmentStartDate () && !params .hasProgram ()) {
422
- violation = "Program must be defined when program enrollment start date is specified" ;
423
- }
424
-
425
- if (params .hasProgramEnrollmentEndDate () && !params .hasProgram ()) {
426
- violation = "Program must be defined when program enrollment end date is specified" ;
427
- }
428
-
429
- if (params .hasProgramIncidentStartDate () && !params .hasProgram ()) {
430
- violation = "Program must be defined when program incident start date is specified" ;
431
- }
432
-
433
- if (params .hasProgramIncidentEndDate () && !params .hasProgram ()) {
434
- violation = "Program must be defined when program incident end date is specified" ;
435
- }
436
-
437
- if (params .hasEventStatus () && (!params .hasEventStartDate () || !params .hasEventEndDate ())) {
438
- violation = "Event start and end date must be specified when event status is specified" ;
439
- }
440
-
441
- if (params .hasLastUpdatedDuration ()
442
- && (params .hasLastUpdatedStartDate () || params .hasLastUpdatedEndDate ())) {
443
- violation =
444
- "Last updated from and/or to and last updated duration cannot be specified simultaneously" ;
445
- }
446
-
447
- if (params .hasLastUpdatedDuration ()
448
- && DateUtils .getDuration (params .getLastUpdatedDuration ()) == null ) {
449
- violation = "Duration is not valid: " + params .getLastUpdatedDuration ();
450
- }
451
-
452
- if (violation != null ) {
453
- log .warn ("Validation failed: " + violation );
454
-
455
- throw new IllegalQueryException (violation );
456
- }
457
- }
458
-
459
- public void validateSearchScope (TrackedEntityQueryParams params ) throws IllegalQueryException {
460
- if (params == null ) {
461
- throw new IllegalQueryException ("Params cannot be null" );
462
- }
463
-
464
- User user = currentUserService .getCurrentUser ();
465
-
466
- if (user == null ) {
467
- throw new IllegalQueryException ("User cannot be null" );
468
- }
469
-
470
- if (!user .isSuper () && user .getOrganisationUnits ().isEmpty ()) {
471
- throw new IllegalQueryException (
472
- "User need to be associated with at least one organisation unit." );
473
- }
474
-
475
- if (!params .hasProgram ()
476
- && !params .hasTrackedEntityType ()
477
- && params .hasFilters ()
478
- && !params .hasOrganisationUnits ()) {
479
- List <String > uniqueAttributeIds =
480
- trackedEntityAttributeService .getAllSystemWideUniqueTrackedEntityAttributes ().stream ()
481
- .map (TrackedEntityAttribute ::getUid )
482
- .toList ();
483
-
484
- for (String att : params .getFilterIds ()) {
485
- if (!uniqueAttributeIds .contains (att )) {
486
- throw new IllegalQueryException (
487
- "Either a program or tracked entity type must be specified" );
488
- }
489
- }
490
- }
491
-
492
- if (!isLocalSearch (params , user )) {
493
- int maxTeiLimit = 0 ; // no limit
494
-
495
- if (params .hasProgram () && params .hasTrackedEntityType ()) {
496
- throw new IllegalQueryException (
497
- "Program and tracked entity cannot be specified simultaneously" );
498
- }
499
-
500
- if (params .hasFilters ()) {
501
- List <String > searchableAttributeIds = new ArrayList <>();
502
-
503
- if (params .hasProgram ()) {
504
- searchableAttributeIds .addAll (params .getProgram ().getSearchableAttributeIds ());
505
- }
506
-
507
- if (params .hasTrackedEntityType ()) {
508
- searchableAttributeIds .addAll (params .getTrackedEntityType ().getSearchableAttributeIds ());
509
- }
510
-
511
- if (!params .hasProgram () && !params .hasTrackedEntityType ()) {
512
- searchableAttributeIds .addAll (
513
- trackedEntityAttributeService .getAllSystemWideUniqueTrackedEntityAttributes ().stream ()
514
- .map (TrackedEntityAttribute ::getUid )
515
- .toList ());
516
- }
517
-
518
- List <String > violatingAttributes = new ArrayList <>();
519
-
520
- for (String attributeId : params .getFilterIds ()) {
521
- if (!searchableAttributeIds .contains (attributeId )) {
522
- violatingAttributes .add (attributeId );
523
- }
524
- }
525
-
526
- if (!violatingAttributes .isEmpty ()) {
527
- throw new IllegalQueryException (
528
- "Non-searchable attribute(s) can not be used during global search: "
529
- + violatingAttributes );
530
- }
531
- }
532
-
533
- if (params .hasTrackedEntityType ()) {
534
- maxTeiLimit = params .getTrackedEntityType ().getMaxTeiCountToReturn ();
535
-
536
- if (!params .hasTrackedEntities () && isTeTypeMinAttributesViolated (params )) {
537
- throw new IllegalQueryException (
538
- "At least "
539
- + params .getTrackedEntityType ().getMinAttributesRequiredToSearch ()
540
- + " attributes should be mentioned in the search criteria." );
541
- }
542
- }
543
-
544
- if (params .hasProgram ()) {
545
- maxTeiLimit = params .getProgram ().getMaxTeiCountToReturn ();
546
-
547
- if (!params .hasTrackedEntities () && isProgramMinAttributesViolated (params )) {
548
- throw new IllegalQueryException (
549
- "At least "
550
- + params .getProgram ().getMinAttributesRequiredToSearch ()
551
- + " attributes should be mentioned in the search criteria." );
552
- }
553
- }
554
-
555
- checkIfMaxTeiLimitIsReached (params , maxTeiLimit );
556
- params .setMaxTeLimit (maxTeiLimit );
557
- }
558
- }
559
-
560
- private boolean isLocalSearch (TrackedEntityQueryParams params , User user ) {
561
- Set <OrganisationUnit > localOrgUnits = user .getOrganisationUnits ();
562
-
563
- Set <OrganisationUnit > searchOrgUnits = new HashSet <>();
564
-
565
- if (params .isOrganisationUnitMode (SELECTED )) {
566
- searchOrgUnits = params .getOrgUnits ();
567
- } else if (params .isOrganisationUnitMode (CHILDREN )
568
- || params .isOrganisationUnitMode (DESCENDANTS )) {
569
- for (OrganisationUnit orgUnit : params .getOrgUnits ()) {
570
- searchOrgUnits .addAll (orgUnit .getChildren ());
571
- }
572
- } else if (params .isOrganisationUnitMode (ALL )) {
573
- searchOrgUnits .addAll (organisationUnitService .getRootOrganisationUnits ());
574
- } else {
575
- searchOrgUnits .addAll (user .getTeiSearchOrganisationUnitsWithFallback ());
576
- }
577
-
578
- for (OrganisationUnit ou : searchOrgUnits ) {
579
- if (!ou .isDescendant (localOrgUnits )) {
580
- return false ;
581
- }
582
- }
583
-
584
- return true ;
585
- }
586
-
587
- private boolean isTeTypeMinAttributesViolated (TrackedEntityQueryParams params ) {
588
- if (params .hasUniqueFilter ()) {
589
- return false ;
590
- }
591
-
592
- return (!params .hasFilters ()
593
- && params .getTrackedEntityType ().getMinAttributesRequiredToSearch () > 0 )
594
- || (params .hasFilters ()
595
- && params .getFilters ().size ()
596
- < params .getTrackedEntityType ().getMinAttributesRequiredToSearch ());
597
- }
598
-
599
- private boolean isProgramMinAttributesViolated (TrackedEntityQueryParams params ) {
600
- if (params .hasUniqueFilter ()) {
601
- return false ;
602
- }
603
-
604
- return (!params .hasFilters () && params .getProgram ().getMinAttributesRequiredToSearch () > 0 )
605
- || (params .hasFilters ()
606
- && params .getFilters ().size () < params .getProgram ().getMinAttributesRequiredToSearch ());
607
- }
608
-
609
- private void checkIfMaxTeiLimitIsReached (TrackedEntityQueryParams params , int maxTeiLimit ) {
610
- if (maxTeiLimit > 0 ) {
611
- int teCount = trackedEntityStore .getTrackedEntityCountWithMaxTrackedEntityLimit (params );
612
-
613
- if (teCount > maxTeiLimit ) {
614
- throw new IllegalQueryException ("maxteicountreached" );
615
- }
616
- }
617
- }
618
-
619
- public int getTrackedEntityCount (
620
- TrackedEntityQueryParams params ,
621
- boolean skipAccessValidation ,
622
- boolean skipSearchScopeValidation ) {
623
- decideAccess (params );
624
-
625
- if (!skipAccessValidation ) {
626
- validate (params );
627
- }
628
-
629
- if (!skipSearchScopeValidation ) {
630
- validateSearchScope (params );
631
- }
632
-
633
- // using countForGrid here to leverage the better performant rewritten
634
- // sql query
635
- return trackedEntityStore .getTrackedEntityCount (params );
636
- }
637
-
638
337
/**
639
338
* We need to return the full models for relationship items (i.e. trackedEntity, enrollment and
640
339
* event) in our API. The aggregate stores currently do not support that, so we need to fetch the
0 commit comments