Repository: corona-warn-app/cwa-server
File: common/persistence/src/main/java/app/coronawarn/server/common/persistence/service/TraceTimeIntervalWarningService.java (Java)

Code before:
  private int saveCheckins(List<CheckIn> checkins, Function<ByteString, byte[]> idHashGenerator) {
    int numberOfInsertedTraceWarnings = 0;

    for (CheckIn checkin : checkins) {
      byte[] hashId = idHashGenerator.apply(checkin.getLocationId());
      if (hashId != NO_HASH) {
        boolean traceWarningInsertedSuccessfully = traceTimeIntervalWarningRepo
            .saveDoNothingOnConflict(hashId,
                checkin.getStartIntervalNumber(),
                checkin.getEndIntervalNumber() - checkin.getStartIntervalNumber(),
                checkin.getTransmissionRiskLevel(),
                CheckinsDateSpecification.HOUR_SINCE_EPOCH_DERIVATION
                    .apply(Instant.now().getEpochSecond()));

        if (traceWarningInsertedSuccessfully) {
          numberOfInsertedTraceWarnings++;
        }
      }
    }

    int conflictingTraceWarnings = checkins.size() - numberOfInsertedTraceWarnings;
    if (conflictingTraceWarnings > 0) {
      logger.warn(
          "{} out of {} TraceTimeIntervalWarnings conflicted with existing "
          + "database entries or had errors while storing "
              + "and were ignored.",
          conflictingTraceWarnings, checkins.size());
    }

    return numberOfInsertedTraceWarnings;
  }

  /**
   * For each checkin in the given list, generate other fake checkin data based on the passed in
   * number and store everything as {@link TraceTimeIntervalWarning} entities. Returns the number of
   * inserted entities which is useful for the case where there might be conflicts with the table
   * constraints during the db save operations.
   */
  @Transactional
  public int saveCheckinsWithFakeData(List<CheckIn> originalCheckins, int numberOfFakesToCreate) {
    List<CheckIn> allCheckins = new ArrayList<>(originalCheckins);
    allCheckins.addAll(fakeCheckinsGenerator.generateFakeCheckins(originalCheckins,
        numberOfFakesToCreate, randomHashPepper()));
    return saveCheckins(allCheckins, this::hashLocationId);
  }

  /**
   * Returns all available {@link TraceTimeIntervalWarning}s sorted by their submissionTimestamp.
   */
  public Collection<TraceTimeIntervalWarning> getTraceTimeIntervalWarning() {
    return StreamUtils

Review comment (suggestion):
Would prefer to ignore on conflict, but reject the whole lot on error (as this might hint at a malicious, manufactured submission).

Code after:
    return saveCheckins(checkins, this::hashLocationId);
  }

  private int saveCheckins(List<CheckIn> checkins, Function<ByteString, byte[]> idHashGenerator) {
    int numberOfInsertedTraceWarnings = 0;

    for (CheckIn checkin : checkins) {
      byte[] hashId = idHashGenerator.apply(checkin.getLocationId());
      boolean traceWarningInsertedSuccessfully = traceTimeIntervalWarningRepo
          .saveDoNothingOnConflict(hashId, checkin.getStartIntervalNumber(),
              checkin.getEndIntervalNumber() - checkin.getStartIntervalNumber(),
              checkin.getTransmissionRiskLevel(),
              CheckinsDateSpecification.HOUR_SINCE_EPOCH_DERIVATION
                  .apply(Instant.now().getEpochSecond()));

      if (traceWarningInsertedSuccessfully) {
        numberOfInsertedTraceWarnings++;
      }
    }

    int conflictingTraceWarnings = checkins.size() - numberOfInsertedTraceWarnings;
    if (conflictingTraceWarnings > 0) {
      logger.warn(
          "{} out of {} TraceTimeIntervalWarnings conflicted with existing "
          + "database entries or had errors while storing "
              + "and were ignored.",
          conflictingTraceWarnings, checkins.size());
    }

    return numberOfInsertedTraceWarnings;
  }

  /**
   * For each checkin in the given list, generate other fake checkin data based on the passed in
   * number and store everything as {@link TraceTimeIntervalWarning} entities. Returns the number of
   * inserted entities which is useful for the case where there might be conflicts with the table
   * constraints during the db save operations.
   */
  @Transactional
  public int saveCheckinsWithFakeData(List<CheckIn> originalCheckins, int numberOfFakesToCreate,
      byte[] pepper) {
    List<CheckIn> allCheckins = new ArrayList<>(originalCheckins);
    allCheckins.addAll(fakeCheckinsGenerator.generateFakeCheckins(originalCheckins,
        numberOfFakesToCreate, pepper));
    return saveCheckins(allCheckins, this::hashLocationId);
  }

  /**
   * Returns all available {@link TraceTimeIntervalWarning}s sorted by their submissionTimestamp.
   */
  public Collection<TraceTimeIntervalWarning> getTraceTimeIntervalWarnings() {