/**
   * Return a list of edorgs authorized to use the app, or null if the application is approved for
   * all edorgs
   *
   * @param clientId
   * @return
   */
  public Set<String> getAuthorizingEdOrgsForApp(String clientId) {
    // This is called before the SLIPrincipal has been set, so use TenantContext to get tenant
    // rather than SLIPrincipal on SecurityContext
    Entity app =
        repo.findOne(
            "application", new NeutralQuery(new NeutralCriteria("client_id", "=", clientId)));

    if (app == null) {
      return Collections.EMPTY_SET;
    }

    if (isAuthorizedForAllEdorgs(app)) {
      LOG.debug("App {} is authorized for all edorgs", clientId);
      return null;
    }

    NeutralQuery appAuthCollQuery =
        new NeutralQuery(new NeutralCriteria("applicationId", "=", app.getEntityId()));
    Entity authEntry = repo.findOne("applicationAuthorization", appAuthCollQuery);
    if (authEntry != null) {
      return AppAuthHelper.getAuthorizedEdOrgIds(authEntry);
    } else {
      return new HashSet<String>();
    }
  }
  @Test
  public void findOneMultipleMatches() {
    repository.deleteAll("student", null);
    DBObject indexKeys = new BasicDBObject("body.firstName", 1);
    mongoTemplate.getCollection("student").ensureIndex(indexKeys);

    Map<String, Object> student = buildTestStudentEntity();
    student.put("firstName", "Jadwiga");
    this.repository.create("student", student);

    student = buildTestStudentEntity();
    student.put("firstName", "Jadwiga");
    this.repository.create("student", student);

    student = buildTestStudentEntity();
    student.put("firstName", "Jadwiga");
    this.repository.create("student", student);

    NeutralQuery neutralQuery = new NeutralQuery();
    neutralQuery.addCriteria(new NeutralCriteria("firstName=Jadwiga"));
    assertNotNull(this.repository.findOne("student", neutralQuery));

    repository.deleteAll("student", null);
    mongoTemplate.getCollection("student").dropIndex(indexKeys);
  }
  @Test
  public void testTimestamps() throws Exception {

    // clean up the existing student data
    repository.deleteAll("student", null);

    // create new student entity
    Map<String, Object> student = buildTestStudentEntity();

    // test save
    Entity saved = repository.create("student", student);

    DateTime created = new DateTime(saved.getMetaData().get(EntityMetadataKey.CREATED.getKey()));
    DateTime updated = new DateTime(saved.getMetaData().get(EntityMetadataKey.UPDATED.getKey()));

    assertEquals(created, updated);

    saved.getBody().put("cityOfBirth", "Evanston");

    // Needs to be here to prevent cases where code execution is so fast,
    // there
    // is no difference between create/update times
    Thread.sleep(2);

    repository.update("student", saved, false);

    updated = new DateTime(saved.getMetaData().get(EntityMetadataKey.UPDATED.getKey()));

    assertTrue(updated.isAfter(created));
  }
 private void clearSafeDeleteGradingPeriodData() {
   repository.deleteAll("gradingPeriod", null);
   repository.deleteAll("session", null);
   repository.deleteAll(MongoRepository.CUSTOM_ENTITY_COLLECTION, null);
   repository.deleteAll("yearlyTranscript", null); // actual mongo
   // collection for grade
   // and reportCard
 }
 @Test
 public void testCreateWithMetadata() {
   repository.deleteAll("student", null);
   Map<String, Object> studentBody = buildTestStudentEntity();
   Map<String, Object> studentMetaData = new HashMap<String, Object>();
   repository.create("student", studentBody, studentMetaData, "student");
   assertEquals(1, repository.count("student", new NeutralQuery()));
 }
  @Test
  public void findOneTestNegative() {
    repository.deleteAll("student", null);
    DBObject indexKeys = new BasicDBObject("body.firstName", 1);
    mongoTemplate.getCollection("student").ensureIndex(indexKeys);

    NeutralQuery neutralQuery = new NeutralQuery();
    neutralQuery.addCriteria(new NeutralCriteria("firstName=Jadwiga"));

    assertNull(this.repository.findOne("student", neutralQuery));

    repository.deleteAll("student", null);
    mongoTemplate.getCollection("student").dropIndex(indexKeys);
  }
  /**
   * Get the list of authorized apps for the user based on the user's LEA.
   *
   * <p>No additional filtering is done on the results. E.g. if a user is a non-admin, the admin
   * apps will still show up in the list, or if an app is disabled it will still show up.
   *
   * @param principal
   * @return list of app IDs, or null if it couldn't be determined
   */
  @SuppressWarnings("unchecked")
  public boolean isAuthorizedForApp(Entity app, SLIPrincipal principal) {

    if (principal.isAdminRealmAuthenticated()) {
      return isAdminVisible(app);
    } else {
      if (isAutoAuthorized(app)) {
        return true;
      } else if (!isOperatorApproved(app)) {
        return false;
      } else {
        Set<String> edOrgs = helper.locateDirectEdorgs(principal.getEntity());
        NeutralQuery appAuthCollQuery = new NeutralQuery();
        appAuthCollQuery.addCriteria(new NeutralCriteria("applicationId", "=", app.getEntityId()));
        appAuthCollQuery.addCriteria(
            new NeutralCriteria("edorgs.authorizedEdorg", NeutralCriteria.CRITERIA_IN, edOrgs));
        Entity authorizedApps = repo.findOne("applicationAuthorization", appAuthCollQuery);
        if (authorizedApps != null) {
          if (isAutoApproved(app)) {
            return true;
          } else {
            // query approved edorgs
            List<String> approvedDistricts =
                new ArrayList<String>((List<String>) app.getBody().get("authorized_ed_orgs"));
            List<String> myDistricts = helper.getDistricts(edOrgs);
            approvedDistricts.retainAll(myDistricts);
            return !approvedDistricts.isEmpty();
          }
        }
      }
    }
    return false;
  }
  private Entity createEducationOrganizationEntity(
      String stateOrgId, String type, String organizationCategory, List<String> parentRefs) {
    Random rand = new Random();
    Map<String, Object> body = new HashMap<String, Object>();
    List<Map<String, String>> addresses = new ArrayList<Map<String, String>>();
    Map<String, String> address = new HashMap<String, String>();
    List<String> organizationCategories = new ArrayList<String>();

    /*
      	    <xs:enumeration value="State Education Agency" />
    <xs:enumeration value="Education Service Center" />
    <xs:enumeration value="Local Education Agency" />
    <xs:enumeration value="School" />
          */
    organizationCategories.add(organizationCategory);
    body.put(ParameterConstants.ORGANIZATION_CATEGORIES, organizationCategories);

    address.put("streetNumberName", rand.nextInt(100) + " Hill Street");
    address.put("city", "My City");
    address.put("stateAbbreviation", "IL");
    address.put("postalCode", "1235");
    addresses.add(address);
    body.put("address", addresses);

    if (parentRefs != null && !parentRefs.isEmpty()) {
      body.put(ParameterConstants.PARENT_EDUCATION_AGENCY_REFERENCE, parentRefs);
    }
    body.put("nameOfInstitution", stateOrgId + "Name");
    body.put(ParameterConstants.STATE_ORGANIZATION_ID, stateOrgId);
    return repository.create(type, body, EntityNames.EDUCATION_ORGANIZATION);
  }
  private String insertAttendanceEventData(boolean skipFirst) {
    // Clear attendance collection.
    repository.deleteAll("attendance", null);

    // Populate the attendance record to be deleted, and add it to the db.
    Map<String, Object> attendanceMap = new HashMap<String, Object>();
    List<Map<String, String>> attendanceEvents = getAttendanceEvents(skipFirst);
    attendanceMap.put("attendanceEvent", attendanceEvents);
    attendanceMap.put("schoolId", "schoolId1");
    attendanceMap.put("schoolYear", "2011-2012");
    attendanceMap.put("studentId", "studentId1");
    repository.create("attendance", attendanceMap);

    // Get the db id of the attendance record; there is only one.
    NeutralQuery neutralQuery = new NeutralQuery();
    Entity attendance = repository.findOne("attendance", neutralQuery);
    return attendance.getEntityId();
  }
  private String prepareSafeDeleteGradingPeriodLeafData() {
    DBObject indexKeys = new BasicDBObject("body.beginDate", 1);
    mongoTemplate.getCollection("gradingPeriod").ensureIndex(indexKeys);

    // create a minimal gradingPeriod document
    Map<String, Object> gradingPeriodIdentity = new HashMap<String, Object>();
    gradingPeriodIdentity.put("gradingPeriod", "gradingPeriod1");
    gradingPeriodIdentity.put("schoolYear", "2011-2012");
    gradingPeriodIdentity.put("schoolId", "schoolId1");
    Map<String, Object> gradingPeriodBody = new HashMap<String, Object>();
    gradingPeriodBody.put("gradingPeriodIdentity", gradingPeriodIdentity);
    gradingPeriodBody.put("beginDate", "beginDate1");
    repository.create("gradingPeriod", gradingPeriodBody);

    // get the db id of the gradingPeriod - there is only one
    NeutralQuery neutralQuery = new NeutralQuery();
    Entity gradingPeriod1 = repository.findOne("gradingPeriod", neutralQuery);
    return gradingPeriod1.getEntityId();
  }
  @Test
  public void testFindIdsByQuery() {
    repository.deleteAll("student", null);
    repository.create("student", buildTestStudentEntity());
    repository.create("student", buildTestStudentEntity());
    repository.create("student", buildTestStudentEntity());
    repository.create("student", buildTestStudentEntity());
    repository.create("student", buildTestStudentEntity());
    NeutralQuery neutralQuery = new NeutralQuery();
    neutralQuery.setOffset(0);
    neutralQuery.setLimit(100);

    Iterable<String> ids = repository.findAllIds("student", neutralQuery);
    List<String> idList = new ArrayList<String>();
    for (String id : ids) {
      idList.add(id);
    }

    assertEquals(5, idList.size());
  }
  @Test
  public void testUpdateRetryWithError() {
    Repository<Entity> mockRepo = Mockito.spy(repository);
    Map<String, Object> studentBody = buildTestStudentEntity();
    Map<String, Object> studentMetaData = new HashMap<String, Object>();
    Entity entity = new MongoEntity("student", null, studentBody, studentMetaData);
    int noOfRetries = 3;

    Mockito.doThrow(new InvalidDataAccessApiUsageException("Test Exception"))
        .when(mockRepo)
        .update("student", entity, false);
    Mockito.doCallRealMethod().when(mockRepo).updateWithRetries("student", entity, noOfRetries);

    try {
      mockRepo.updateWithRetries("student", entity, noOfRetries);
    } catch (InvalidDataAccessApiUsageException ex) {
      assertEquals(ex.getMessage(), "Test Exception");
    }

    Mockito.verify(mockRepo, Mockito.times(noOfRetries)).update("student", entity, false);
  }
  @Test
  public void testUpdateRetry() {
    TenantContext.setTenantId("SLIUnitTest");
    repository.deleteAll("student", null);

    DBObject indexKeys = new BasicDBObject("body.cityOfBirth", 1);
    mongoTemplate.getCollection("student").ensureIndex(indexKeys);

    repository.create("student", buildTestStudentEntity());

    Entity entity = repository.findOne("student", new NeutralQuery());
    Map<String, Object> studentBody = entity.getBody();
    studentBody.put("cityOfBirth", "ABC");

    Entity studentEntity =
        new MongoEntity("student", entity.getEntityId(), studentBody, entity.getMetaData());
    repository.updateWithRetries("student", studentEntity, 5);

    NeutralQuery neutralQuery = new NeutralQuery();
    neutralQuery.addCriteria(new NeutralCriteria("cityOfBirth=ABC"));
    assertEquals(1, repository.count("student", neutralQuery));

    repository.deleteAll("student", null);
    mongoTemplate.getCollection("student").dropIndex(indexKeys);
  }
 boolean schoolLineageIs(String schoolId, Set<String> expectedEdOrgs) {
   NeutralQuery neutralQuery = new NeutralQuery();
   neutralQuery.addCriteria(new NeutralCriteria("_id", NeutralCriteria.OPERATOR_EQUAL, schoolId));
   Entity school = repository.findOne(EntityNames.EDUCATION_ORGANIZATION, neutralQuery);
   ArrayList<String> edOrgs = (ArrayList<String>) school.getMetaData().get("edOrgs");
   if (edOrgs == null) {
     return expectedEdOrgs.isEmpty();
   } else if (!expectedEdOrgs.equals(new HashSet<String>(edOrgs))) {
     System.out.println(
         "School edOrg lineage incorrect. Expected " + expectedEdOrgs + ", got " + edOrgs);
     return false;
   }
   return true;
 }
 @Test
 public void testDeleteLastAttendanceEvent() {
   String attendanceRecordId = insertAttendanceEventData(true);
   Entity deleteAssessment =
       createDeleteAttendanceEntity(
           "Missed school bus", "Tardy", "2011-10-26", attendanceRecordId);
   CascadeResult deleteResult2 =
       repository.safeDelete(
           deleteAssessment, attendanceRecordId, false, false, false, false, 1, null);
   Entity attendanceRecord = getAttendanceRecord(attendanceRecordId);
   Assert.assertEquals(
       "Delete result should be success", CascadeResult.Status.SUCCESS, deleteResult2.getStatus());
   Assert.assertNull("Attendance record should not exist in DB", attendanceRecord);
 }
  @Test
  public void testCreateRetryWithError() {
    Repository<Entity> mockRepo = Mockito.spy(repository);
    Map<String, Object> studentBody = buildTestStudentEntity();
    Map<String, Object> studentMetaData = new HashMap<String, Object>();
    int noOfRetries = 5;

    Mockito.doThrow(new MongoException("Test Exception"))
        .when(((MongoEntityRepository) mockRepo))
        .internalCreate("student", null, studentBody, studentMetaData, "student");
    Mockito.doCallRealMethod()
        .when(mockRepo)
        .createWithRetries("student", null, studentBody, studentMetaData, "student", noOfRetries);

    try {
      mockRepo.createWithRetries(
          "student", null, studentBody, studentMetaData, "student", noOfRetries);
    } catch (MongoException ex) {
      assertEquals(ex.getMessage(), "Test Exception");
    }

    Mockito.verify((MongoEntityRepository) mockRepo, Mockito.times(noOfRetries))
        .internalCreate("student", null, studentBody, studentMetaData, "student");
  }
 @Test
 public void testDeleteNextToLastAttendanceEvent() {
   String attendanceRecordId = insertAttendanceEventData(false);
   Entity deleteAssessment =
       createDeleteAttendanceEntity(
           "Excused: sick", "Excused Absence", "2012-04-18", attendanceRecordId);
   CascadeResult deleteResult1 =
       repository.safeDelete(
           deleteAssessment, attendanceRecordId, false, false, false, false, 1, null);
   List<Map<String, String>> attendanceEvents = getAttendanceEvents(true);
   Entity attendanceRecord = getAttendanceRecord(attendanceRecordId);
   Assert.assertEquals(
       "Delete result should be success", CascadeResult.Status.SUCCESS, deleteResult1.getStatus());
   Assert.assertNotNull("Attendance record should exist in DB", attendanceRecord);
   Assert.assertEquals(
       "Field attendanceEvent mismatch",
       attendanceEvents,
       attendanceRecord.getBody().get("attendanceEvent"));
 }
 @Test
 public void testDeleteAttendanceEventWithBogusId() {
   // Result is error, and Attendance record should remain unchanged in DB.
   String attendanceRecordId = insertAttendanceEventData(false);
   String bogusAttendanceRecordId = "123-abc-789-def-e1e10";
   Entity bogusDeleteAssessment =
       createDeleteAttendanceEntity(
           "Excused: sick", "Excused Absence", "2012-04-18", bogusAttendanceRecordId);
   CascadeResult bogusDeleteResult =
       repository.safeDelete(
           bogusDeleteAssessment, bogusAttendanceRecordId, false, false, false, false, 1, null);
   List<Map<String, String>> attendanceEvents = getAttendanceEvents(false);
   Entity attendanceRecord = getAttendanceRecord(attendanceRecordId);
   Assert.assertEquals(
       "Delete result should be error", CascadeResult.Status.ERROR, bogusDeleteResult.getStatus());
   Assert.assertNotNull("Attendance record should exist in DB", attendanceRecord);
   Assert.assertEquals(
       "Field attendanceEvent mismatch",
       attendanceEvents,
       attendanceRecord.getBody().get("attendanceEvent"));
 }
  @Test
  public void testDeleteAll() {
    repository.deleteAll("student", null);

    DBObject indexKeys = new BasicDBObject("body.firstName", 1);
    mongoTemplate.getCollection("student").ensureIndex(indexKeys);

    Map<String, Object> studentMap = buildTestStudentEntity();
    studentMap.put("firstName", "John");
    repository.create("student", buildTestStudentEntity());
    repository.create("student", buildTestStudentEntity());
    repository.create("student", buildTestStudentEntity());
    repository.create("student", buildTestStudentEntity());
    repository.create("student", studentMap);
    assertEquals(5, repository.count("student", new NeutralQuery()));
    NeutralQuery neutralQuery = new NeutralQuery();
    neutralQuery.addCriteria(new NeutralCriteria("firstName=John"));
    repository.deleteAll("student", neutralQuery);
    assertEquals(4, repository.count("student", new NeutralQuery()));

    repository.deleteAll("student", null);
    mongoTemplate.getCollection("student").dropIndex(indexKeys);
  }
 @Test
 public void testDeleteAttendanceEventWithDiffNonKeyField() {
   // Result is success, Attendance record should have natural key match attendanceEvent deleted in
   // DB.
   String attendanceRecordId = insertAttendanceEventData(false);
   Entity bogusDeleteAttendance =
       createDeleteAttendanceEntity(
           "Excused: dead", "Excused Absence", "2012-04-18", attendanceRecordId);
   CascadeResult bogusDeleteResult =
       repository.safeDelete(
           bogusDeleteAttendance, attendanceRecordId, false, false, false, false, 1, null);
   List<Map<String, String>> attendanceEvents = getAttendanceEvents(true);
   Entity attendanceRecord = getAttendanceRecord(attendanceRecordId);
   Assert.assertEquals(
       "Delete result should be success",
       CascadeResult.Status.SUCCESS,
       bogusDeleteResult.getStatus());
   Assert.assertNotNull("Attendance record should exist in DB", attendanceRecord);
   Assert.assertEquals(
       "Field attendanceEvent mismatch",
       attendanceEvents,
       attendanceRecord.getBody().get("attendanceEvent"));
 }
  @Test
  public void testCount() {
    repository.deleteAll("student", null);

    DBObject indexKeys = new BasicDBObject("body.cityOfBirth", 1);
    mongoTemplate.getCollection("student").ensureIndex(indexKeys);

    repository.create("student", buildTestStudentEntity());
    repository.create("student", buildTestStudentEntity());
    repository.create("student", buildTestStudentEntity());
    repository.create("student", buildTestStudentEntity());
    Map<String, Object> oddStudent = buildTestStudentEntity();
    oddStudent.put("cityOfBirth", "Nantucket");
    repository.create("student", oddStudent);
    assertEquals(5, repository.count("student", new NeutralQuery()));
    NeutralQuery neutralQuery = new NeutralQuery();
    neutralQuery.addCriteria(new NeutralCriteria("cityOfBirth=Nantucket"));
    assertEquals(1, repository.count("student", neutralQuery));

    repository.deleteAll("student", null);
    mongoTemplate.getCollection("student").dropIndex(indexKeys);
  }
 private Entity getAttendanceRecord(String id) {
   return repository.findById("attendance", id);
 }
 private void clearSchoolLineage(Entity school) {
   school.getMetaData().remove("edOrgs");
   repository.update(EntityNames.EDUCATION_ORGANIZATION, school, false);
 }
  @Test
  public void testCRUDEntityRepository() {

    // clean up the existing student data
    repository.deleteAll("student", null);

    // create new student entity
    Map<String, Object> student = buildTestStudentEntity();

    // test save
    Entity saved = repository.create("student", student);
    String id = saved.getEntityId();
    assertTrue(!id.equals(""));

    // test findAll
    NeutralQuery neutralQuery = new NeutralQuery();
    neutralQuery.setOffset(0);
    neutralQuery.setLimit(20);
    Iterable<Entity> entities = repository.findAll("student", neutralQuery);
    assertNotNull(entities);
    Entity found = entities.iterator().next();
    assertEquals(found.getBody().get("birthDate"), student.get("birthDate"));
    assertEquals((found.getBody()).get("firstName"), "Jane");
    assertEquals((found.getBody()).get("lastName"), "Doe");

    // test find by id
    Entity foundOne = repository.findById("student", saved.getEntityId());
    assertNotNull(foundOne);
    assertEquals(foundOne.getBody().get("birthDate"), student.get("birthDate"));
    assertEquals((found.getBody()).get("firstName"), "Jane");

    // test update
    found.getBody().put("firstName", "Mandy");
    assertTrue(repository.update("student", found, false));
    entities = repository.findAll("student", neutralQuery);
    assertNotNull(entities);
    Entity updated = entities.iterator().next();
    assertEquals(updated.getBody().get("firstName"), "Mandy");

    // test delete by id
    Map<String, Object> student2Body = buildTestStudentEntity();
    Entity student2 = repository.create("student", student2Body);
    entities = repository.findAll("student", neutralQuery);
    assertNotNull(entities.iterator().next());
    repository.delete("student", student2.getEntityId());
    Entity zombieStudent = repository.findById("student", student2.getEntityId());
    assertNull(zombieStudent);

    assertFalse(repository.delete("student", student2.getEntityId()));

    // test deleteAll by entity type
    repository.deleteAll("student", null);
    entities = repository.findAll("student", neutralQuery);
    assertFalse(entities.iterator().hasNext());
  }
  @Test
  public void testNeedsId() {

    Entity e = new MongoEntity("student", buildTestStudentEntity());
    assertFalse(repository.update("student", e, false));
  }
  private void prepareSafeDeleteGradingPeriodReferenceData(String idToDelete) {
    DBObject indexKeys = new BasicDBObject("body.gradingPeriodId", 1);
    mongoTemplate.getCollection("grade").ensureIndex(indexKeys);
    mongoTemplate.getCollection("gradeBookEntry").ensureIndex(indexKeys);
    mongoTemplate.getCollection("reportCard").ensureIndex(indexKeys);
    mongoTemplate
        .getCollection(MongoRepository.CUSTOM_ENTITY_COLLECTION)
        .ensureIndex("metaData." + MongoRepository.CUSTOM_ENTITY_ENTITY_ID);
    DBObject indexKeysList = new BasicDBObject("body.gradingPeriodReference", 1);
    mongoTemplate.getCollection("session").ensureIndex(indexKeysList);

    // add a custom entity referencing the entity to be deleted
    Map<String, Object> customEntityMetaData = new HashMap<String, Object>();
    customEntityMetaData.put(MongoRepository.CUSTOM_ENTITY_ENTITY_ID, idToDelete);
    Map<String, Object> customEntityBody = new HashMap<String, Object>();
    customEntityBody.put("customBodyData", "customData1");
    repository.create(
        MongoRepository.CUSTOM_ENTITY_COLLECTION,
        customEntityBody,
        customEntityMetaData,
        MongoRepository.CUSTOM_ENTITY_COLLECTION);
    customEntityMetaData.put(MongoRepository.CUSTOM_ENTITY_ENTITY_ID, "nonMatchingId");
    customEntityBody.put("customBodyData", "customData2");
    repository.create(
        MongoRepository.CUSTOM_ENTITY_COLLECTION,
        customEntityBody,
        customEntityMetaData,
        MongoRepository.CUSTOM_ENTITY_COLLECTION);

    // add referencing grade entities
    Map<String, Object> gradeMap = new HashMap<String, Object>();
    gradeMap.put("studentId", "studentId1");
    gradeMap.put("sectionId", "sectionId1");
    gradeMap.put("schoolYear", "2011-2012");
    gradeMap.put("gradingPeriodId", "noMatch");
    repository.create("grade", gradeMap); // add one non-matching document
    gradeMap.put("studentId", "studentId2");
    gradeMap.put("sectionId", "sectionId2");
    gradeMap.put("schoolYear", "2011-2012");
    gradeMap.put("gradingPeriodId", idToDelete);
    repository.create("grade", gradeMap); // add matching document

    // // add referencing gradeBookEntry entities - excluded since the
    // reference type is the same as grade
    // Map<String, Object> gradeBookEntryMap = new HashMap<String,
    // Object>();
    // gradeBookEntryMap.put("gradebookEntryType", "gradebookEntryType1");
    // gradeBookEntryMap.put("sectionId", "sectionId1");
    // gradeBookEntryMap.put("gradingPeriodId", idToDelete);
    // repository.create("gradeBookEntry", gradeBookEntryMap); // add one
    // non-matching document
    // gradeBookEntryMap.put("gradebookEntryType", "gradebookEntryType2");
    // gradeBookEntryMap.put("sectionId", "sectionId2");
    // gradeBookEntryMap.put("gradingPeriodId", "noMatch");
    // repository.create("gradeBookEntry", gradeBookEntryMap); // add
    // matching document

    // add referencing resportCard entities
    Map<String, Object> reportCardMap = new HashMap<String, Object>();
    reportCardMap.put("schoolYear", "2011-2012");
    reportCardMap.put("studentId", "studentId1");
    reportCardMap.put("gradingPeriodId", "noMatch");
    repository.create("reportCard", reportCardMap); // add one non-matching
    // document
    reportCardMap.put("schoolYear", "2011-2012");
    reportCardMap.put("studentId", "studentId2");
    reportCardMap.put("gradingPeriodId", idToDelete);
    repository.create("reportCard", reportCardMap); // add matching document

    // create a minimal session document
    Map<String, Object> sessionMap = new HashMap<String, Object>();
    sessionMap.put("sessionName", "session1");
    sessionMap.put("schoolId", "schoolId1");
    List<String> gradingPeriodRefArray = new ArrayList<String>();
    gradingPeriodRefArray.add("dog");
    sessionMap.put("gradingPeriodReference", gradingPeriodRefArray);
    repository.create("session", sessionMap);
    sessionMap.put("sessionName", "session2");
    sessionMap.put("schoolId", "schoolId2");
    gradingPeriodRefArray.add(idToDelete);
    gradingPeriodRefArray.add("mousearama");
    sessionMap.put("gradingPeriodReference", gradingPeriodRefArray);
    repository.create("session", sessionMap);
  }
  @Test
  public void testSchoolLineage() {
    NeutralQuery query = null;

    repository.deleteAll(EntityNames.EDUCATION_ORGANIZATION, null);

    DBObject indexKeys = new BasicDBObject("body." + ParameterConstants.STATE_ORGANIZATION_ID, 1);
    mongoTemplate.getCollection(EntityNames.EDUCATION_ORGANIZATION).ensureIndex(indexKeys);
    mongoTemplate
        .getCollection(EntityNames.EDUCATION_ORGANIZATION)
        .ensureIndex(new BasicDBObject("metaData.edOrgs", 1), new BasicDBObject("sparse", true));

    // Add a school
    Entity school = createEducationOrganizationEntity("school1", "school", "School", null);
    Set<String> expectedEdOrgs = new HashSet();
    expectedEdOrgs.add(school.getEntityId());
    assertTrue(
        "School not found in lineage.", schoolLineageIs(school.getEntityId(), expectedEdOrgs));

    // Add an SEA
    Entity sea =
        createEducationOrganizationEntity(
            "sea1", "stateEducationAgency", "State Education Agency", null);
    assertTrue(
        "After adding SEA expected edOrgs not found in lineage.",
        schoolLineageIs(school.getEntityId(), expectedEdOrgs));

    // Add an LEA
    List<String> parentRefs = new ArrayList<String>(Arrays.asList(sea.getEntityId()));
    Entity lea =
        createEducationOrganizationEntity(
            "lea1", "localEducationAgency", "Local Education Agency", parentRefs);
    assertTrue(
        "After adding LEA expected edOrgs not found in lineage.",
        schoolLineageIs(school.getEntityId(), expectedEdOrgs));

    // doUpdate School parent ref to LEA
    List<String> parentRefsUpdate = new ArrayList<String>(Arrays.asList(lea.getEntityId()));
    query =
        new NeutralQuery(
            new NeutralCriteria("_id", NeutralCriteria.OPERATOR_EQUAL, school.getEntityId()));
    Update update =
        new Update()
            .set("body." + ParameterConstants.PARENT_EDUCATION_AGENCY_REFERENCE, parentRefsUpdate);
    repository.doUpdate(EntityNames.EDUCATION_ORGANIZATION, query, update);
    expectedEdOrgs.add(lea.getEntityId());
    expectedEdOrgs.add(sea.getEntityId());
    assertTrue(
        "After updating school parent ref expected edOrgs not found in lineage.",
        schoolLineageIs(school.getEntityId(), expectedEdOrgs));

    // Patch LEA parent ref to an undefined id
    List<String> parentRefsPatch = new ArrayList<String>(Arrays.asList("undefinedId"));
    Map<String, Object> newValues = new HashMap<String, Object>();
    newValues.put(ParameterConstants.PARENT_EDUCATION_AGENCY_REFERENCE, parentRefsPatch);
    repository.patch(
        "localEducationEntity", EntityNames.EDUCATION_ORGANIZATION, lea.getEntityId(), newValues);
    expectedEdOrgs.remove(sea.getEntityId());
    assertTrue(
        "After updating school parent ref expected edOrgs not found in lineage.",
        schoolLineageIs(school.getEntityId(), expectedEdOrgs));

    // Update LEA to set parent ref back to SEA
    repository.update(EntityNames.EDUCATION_ORGANIZATION, lea, false);
    expectedEdOrgs.add(sea.getEntityId());
    assertTrue(
        "After updating school parent ref expected edOrgs not found in lineage.",
        schoolLineageIs(school.getEntityId(), expectedEdOrgs));

    // Delete LEA - lineage should be recalculated
    repository.delete(EntityNames.EDUCATION_ORGANIZATION, lea.getEntityId());
    expectedEdOrgs.remove(lea.getEntityId());
    expectedEdOrgs.remove(sea.getEntityId());
    assertTrue(
        "After deleting lea expected edOrgs not found in lineage.",
        schoolLineageIs(school.getEntityId(), expectedEdOrgs));

    // Insert LEA with no parent ref to SEA
    lea.getBody().remove(ParameterConstants.PARENT_EDUCATION_AGENCY_REFERENCE);
    Entity insertedLea =
        ((MongoEntityRepository) repository).insert(lea, EntityNames.EDUCATION_ORGANIZATION);
    expectedEdOrgs.add(lea.getEntityId());
    assertTrue(
        "After re-adding LEA with no parent ref expected edOrgs not found in lineage.",
        schoolLineageIs(school.getEntityId(), expectedEdOrgs));

    // findAndUpdate School parent ref to SEA
    List<String> parentRefsSEA = new ArrayList<String>(Arrays.asList(sea.getEntityId()));
    query =
        new NeutralQuery(
            new NeutralCriteria("_id", NeutralCriteria.OPERATOR_EQUAL, school.getEntityId()));
    update =
        new Update()
            .set("body." + ParameterConstants.PARENT_EDUCATION_AGENCY_REFERENCE, parentRefsSEA);
    repository.findAndUpdate(EntityNames.EDUCATION_ORGANIZATION, query, update);
    expectedEdOrgs.remove(lea.getEntityId());
    expectedEdOrgs.add(sea.getEntityId());
    assertTrue(
        "After updating school parent ref to SEA, expected edOrgs not found in lineage.",
        schoolLineageIs(school.getEntityId(), expectedEdOrgs));

    // Clear lineage for negative tests
    clearSchoolLineage(school);

    // Create an unrelated entity type and make sure school lineage isn't recalculated
    repository.create("student", buildTestStudentEntity());
    assertTrue(
        "After adding a student lineage school lineage should not change.",
        schoolLineageIs(school.getEntityId(), new HashSet<String>()));

    // Patch an edOrg non-parent-ref and make sure school lineage isn't recalculated
    newValues = new HashMap<String, Object>();
    newValues.put("body.nameOfInstitution", "updatedName");
    repository.patch("school", EntityNames.EDUCATION_ORGANIZATION, school.getEntityId(), newValues);
    assertTrue(
        "Updating a school non-parent ref should not change school lineage.",
        schoolLineageIs(school.getEntityId(), new HashSet<String>()));

    mongoTemplate.getCollection(EntityNames.EDUCATION_ORGANIZATION).drop();
  }
  private void testSafeDeleteHelper(
      String entityType,
      String overridingId,
      boolean cascade,
      boolean dryrun,
      boolean forced,
      boolean logViolations,
      Integer maxObjects,
      AccessibilityCheck access,
      boolean leafDataOnly,
      int expectedNObjects,
      int expectedDepth,
      CascadeResult.Status expectedStatus,
      CascadeResultError.ErrorType expectedErrorType,
      CascadeResultError.ErrorType expectedWarningType) {
    //        System.out.println("Testing safeDelete: ");
    //        System.out.println("   entity type             : " + entityType);
    //        System.out.println("   override id             : " + overridingId);
    //        System.out.println("   cascade                 : " + cascade);
    //        System.out.println("   dryrun                  : " + dryrun);
    //        System.out.println("   maxObjects              : " + maxObjects);
    //        System.out.println("   leaf data only          : " + leafDataOnly);
    //        System.out.println("   expected affected count : " + expectedNObjects);
    //        System.out.println("   expected depth          : " + expectedDepth);

    CascadeResult result = null;
    String idToDelete = prepareSafeDeleteGradingPeriodData(leafDataOnly);

    // used to test bad id scenario
    if (overridingId != null) {
      idToDelete = overridingId;
    }

    result =
        repository.safeDelete(
            entityType, idToDelete, cascade, dryrun, forced, logViolations, maxObjects, access);

    // check for at least one instance of the expected error type
    boolean errorMatchFound = false;
    List<CascadeResultError> errors = result.getErrors();
    if (expectedErrorType == null && errors != null && errors.size() == 0) {
      errorMatchFound = true;
    } else {
      for (CascadeResultError error : errors) {
        if (error.getErrorType() == expectedErrorType) {
          errorMatchFound = true;
          break;
        }
      }
    }

    //        for (CascadeResultError error : result.getErrors()) {
    //            System.out.println(error);
    //        }

    // check for at least one instance of the expected warning type
    boolean warningMatchFound = false;
    List<CascadeResultError> warnings = result.getWarnings();
    if (expectedWarningType == null && warnings != null && warnings.size() == 0) {
      warningMatchFound = true;
    } else {
      for (CascadeResultError warning : warnings) {
        if (warning.getErrorType() == expectedWarningType) {
          warningMatchFound = true;
          break;
        }
      }
    }

    //        for(CascadeResultError warning : result.getWarnings()) {
    //            System.out.println(warning);
    //        }

    //   verify expected results
    assertEquals(expectedNObjects, result.getnObjects());
    assertEquals(expectedDepth, result.getDepth());
    assertEquals(expectedStatus, result.getStatus());
    assertTrue(errorMatchFound);
    assertTrue(warningMatchFound);
  }
  @SuppressWarnings("unchecked")
  @Test
  public void testSort() {

    // clean up the existing student data
    repository.deleteAll("student", null);

    // create new student entity
    Map<String, Object> body1 = buildTestStudentEntity();
    Map<String, Object> body2 = buildTestStudentEntity();
    Map<String, Object> body3 = buildTestStudentEntity();
    Map<String, Object> body4 = buildTestStudentEntity();

    body1.put("firstName", "Austin");
    body2.put("firstName", "Jane");
    body3.put("firstName", "Mary");
    body4.put("firstName", "Suzy");

    body1.put("performanceLevels", new String[] {"1"});
    body2.put("performanceLevels", new String[] {"2"});
    body3.put("performanceLevels", new String[] {"3"});
    body4.put("performanceLevels", new String[] {"4"});

    // save entities
    repository.create("student", body1);
    repository.create("student", body2);
    repository.create("student", body3);
    repository.create("student", body4);

    // sort entities by firstName with ascending order
    NeutralQuery sortQuery1 = new NeutralQuery();
    sortQuery1.setSortBy("firstName");
    sortQuery1.setSortOrder(NeutralQuery.SortOrder.ascending);
    sortQuery1.setOffset(0);
    sortQuery1.setLimit(100);

    Iterable<Entity> entities = repository.findAll("student", sortQuery1);
    assertNotNull(entities);
    Iterator<Entity> it = entities.iterator();
    assertEquals("Austin", it.next().getBody().get("firstName"));
    assertEquals("Jane", it.next().getBody().get("firstName"));
    assertEquals("Mary", it.next().getBody().get("firstName"));
    assertEquals("Suzy", it.next().getBody().get("firstName"));

    // sort entities by firstName with descending order
    NeutralQuery sortQuery2 = new NeutralQuery();
    sortQuery2.setSortBy("firstName");
    sortQuery2.setSortOrder(NeutralQuery.SortOrder.descending);
    sortQuery2.setOffset(0);
    sortQuery2.setLimit(100);
    entities = repository.findAll("student", sortQuery2);
    assertNotNull(entities);
    it = entities.iterator();
    assertEquals("Suzy", it.next().getBody().get("firstName"));
    assertEquals("Mary", it.next().getBody().get("firstName"));
    assertEquals("Jane", it.next().getBody().get("firstName"));
    assertEquals("Austin", it.next().getBody().get("firstName"));

    // sort entities by performanceLevels which is an array with ascending
    // order
    NeutralQuery sortQuery3 = new NeutralQuery();
    sortQuery3.setSortBy("performanceLevels");
    sortQuery3.setSortOrder(NeutralQuery.SortOrder.ascending);
    sortQuery3.setOffset(0);
    sortQuery3.setLimit(100);
    entities = repository.findAll("student", sortQuery3);
    assertNotNull(entities);
    it = entities.iterator();
    assertEquals("1", ((List<String>) (it.next().getBody().get("performanceLevels"))).get(0));
    assertEquals("2", ((List<String>) (it.next().getBody().get("performanceLevels"))).get(0));
    assertEquals("3", ((List<String>) (it.next().getBody().get("performanceLevels"))).get(0));
    assertEquals("4", ((List<String>) (it.next().getBody().get("performanceLevels"))).get(0));

    // sort entities by performanceLevels which is an array with descending
    // order
    NeutralQuery sortQuery4 = new NeutralQuery();
    sortQuery4.setSortBy("performanceLevels");
    sortQuery4.setSortOrder(NeutralQuery.SortOrder.descending);
    sortQuery4.setOffset(0);
    sortQuery4.setLimit(100);
    entities = repository.findAll("student", sortQuery4);
    assertNotNull(entities);
    it = entities.iterator();
    assertEquals("4", ((List<String>) (it.next().getBody().get("performanceLevels"))).get(0));
    assertEquals("3", ((List<String>) (it.next().getBody().get("performanceLevels"))).get(0));
    assertEquals("2", ((List<String>) (it.next().getBody().get("performanceLevels"))).get(0));
    assertEquals("1", ((List<String>) (it.next().getBody().get("performanceLevels"))).get(0));
  }
  @Override
  public SLIPrincipal locate(
      String tenantId, String externalUserId, String userType, String clientId) {
    LOG.info("Locating user {}@{} of type: {}", new Object[] {externalUserId, tenantId, userType});
    SLIPrincipal user = new SLIPrincipal(externalUserId + "@" + tenantId);
    user.setExternalId(externalUserId);
    user.setTenantId(tenantId);
    user.setUserType(userType);

    TenantContext.setTenantId(tenantId);

    if (EntityNames.STUDENT.equals(userType)) {
      NeutralQuery neutralQuery =
          new NeutralQuery(
              new NeutralCriteria(
                  ParameterConstants.STUDENT_UNIQUE_STATE_ID,
                  NeutralCriteria.OPERATOR_EQUAL,
                  externalUserId));
      neutralQuery.setOffset(0);
      neutralQuery.setLimit(1);
      user.setEntity(repo.findOne(EntityNames.STUDENT, neutralQuery, true));
    } else if (EntityNames.PARENT.equals(userType)) {
      NeutralQuery neutralQuery =
          new NeutralQuery(
              new NeutralCriteria(
                  ParameterConstants.PARENT_UNIQUE_STATE_ID,
                  NeutralCriteria.OPERATOR_EQUAL,
                  externalUserId));
      neutralQuery.setOffset(0);
      neutralQuery.setLimit(1);
      user.setEntity(repo.findOne(EntityNames.PARENT, neutralQuery, true));
    } else if (isStaff(userType)) {

      NeutralQuery neutralQuery = new NeutralQuery();
      neutralQuery.setOffset(0);
      neutralQuery.setLimit(1);
      neutralQuery.addCriteria(
          new NeutralCriteria(
              ParameterConstants.STAFF_UNIQUE_STATE_ID,
              NeutralCriteria.OPERATOR_EQUAL,
              externalUserId));

      Iterable<Entity> staff = repo.findAll(EntityNames.STAFF, neutralQuery);

      if (staff != null && staff.iterator().hasNext()) {
        Entity entity = staff.iterator().next();
        Set<String> edorgs = edorgHelper.locateDirectEdorgs(entity);
        if (edorgs.size() == 0) {
          LOG.warn("User {} is not currently associated to a school/edorg", user.getId());
          throw new APIAccessDeniedException(
              "User is not currently associated to a school/edorg", user, clientId);
        }
        user.setEntity(entity);
      }
    }

    if (user.getEntity() == null) {
      LOG.warn("Failed to locate user {} in the datastore", user.getId());
      Entity entity =
          new MongoEntity(
              "user",
              SLIPrincipal.NULL_ENTITY_ID,
              new HashMap<String, Object>(),
              new HashMap<String, Object>());
      user.setEntity(entity);
    } else {
      LOG.info(
          "Matched user: {}@{} -> {}",
          new Object[] {externalUserId, tenantId, user.getEntity().getEntityId()});
    }

    return user;
  }