From 1b54a11a0b125441126983b588450e32e2f5cfdf Mon Sep 17 00:00:00 2001 From: "Svetlana.Zemlyanskaya" Date: Wed, 12 Jun 2019 15:00:09 +0200 Subject: [PATCH] FUS: add custom validator to compare reported values with a file (for WI-47009) GitOrigin-RevId: d05528f4bd9338ed6193c49c5b26b2c6aa2912cd --- .../impl/LocalFileCustomWhiteListRule.java | 106 ++++++++++++++++++ .../LocalFileWhitelistValidatorTest.kt | 60 ++++++++++ .../validator/file-with-allowed-values.txt | 2 + 3 files changed, 168 insertions(+) create mode 100644 platform/platform-impl/src/com/intellij/internal/statistic/eventLog/validator/rules/impl/LocalFileCustomWhiteListRule.java create mode 100644 platform/platform-tests/testSrc/com/intellij/internal/statistics/validator/LocalFileWhitelistValidatorTest.kt create mode 100644 platform/platform-tests/testSrc/com/intellij/internal/statistics/validator/file-with-allowed-values.txt diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/validator/rules/impl/LocalFileCustomWhiteListRule.java b/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/validator/rules/impl/LocalFileCustomWhiteListRule.java new file mode 100644 index 000000000000..58522d5dc805 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/internal/statistic/eventLog/validator/rules/impl/LocalFileCustomWhiteListRule.java @@ -0,0 +1,106 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.internal.statistic.eventLog.validator.rules.impl; + +import com.intellij.internal.statistic.eventLog.validator.ValidationResultType; +import com.intellij.internal.statistic.eventLog.validator.rules.EventContext; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.reference.SoftReference; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public abstract class LocalFileCustomWhiteListRule extends CustomWhiteListRule { + private static final Logger LOG = Logger.getInstance(LocalFileCustomWhiteListRule.class); + + private WeakReference myWhitelistRef; + private final String myRuleId; + private final Class myResourceHolder; + private final String myRelativePath; + + protected LocalFileCustomWhiteListRule(@NotNull String ruleId, @NotNull Class resource, @NotNull String path) { + myRuleId = ruleId; + myResourceHolder = resource; + myRelativePath = path; + } + + @Override + public boolean acceptRuleId(@Nullable String ruleId) { + return myRuleId.equals(ruleId); + } + + private boolean isWhitelisted(@NotNull String value) { + final CachedWhitelistedItems whitelist = getWhitelist(); + return whitelist.contains(value); + } + + @NotNull + private synchronized CachedWhitelistedItems getWhitelist() { + final CachedWhitelistedItems whitelist = SoftReference.dereference(myWhitelistRef); + if (whitelist != null) { + return whitelist; + } + final CachedWhitelistedItems items = create(); + myWhitelistRef = new WeakReference<>(items); + return items; + } + + @NotNull + private CachedWhitelistedItems create() { + try { + final URL stream = myResourceHolder.getResource(myRelativePath); + if (stream == null) { + throw new IOException("Resource " + myRelativePath + " not found"); + } + + final List values = FileUtil.loadLines(new File(stream.toURI())); + if (!values.isEmpty()) { + return CachedWhitelistedItems.create(ContainerUtil.map2Set(values, value -> value.trim())); + } + } + catch (IOException | URISyntaxException e) { + LOG.info(e); + } + return CachedWhitelistedItems.empty(); + } + + @NotNull + @Override + final protected ValidationResultType doValidate(@NotNull String data, @NotNull EventContext context) { + if (isThirdPartyValue(data) || isWhitelisted(data)) { + return ValidationResultType.ACCEPTED; + } + return ValidationResultType.REJECTED; + } + + private static class CachedWhitelistedItems { + private final Set myValues; + + private CachedWhitelistedItems(@NotNull Set values) { + myValues = values; + } + + public boolean contains(@NotNull String value) { + return myValues.contains(value); + } + + @NotNull + public static CachedWhitelistedItems create(@NotNull Set values) { + return new CachedWhitelistedItems(values); + } + + @NotNull + public static CachedWhitelistedItems empty() { + return new CachedWhitelistedItems(Collections.emptySet()); + } + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/validator/LocalFileWhitelistValidatorTest.kt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/validator/LocalFileWhitelistValidatorTest.kt new file mode 100644 index 000000000000..13c95457595b --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/validator/LocalFileWhitelistValidatorTest.kt @@ -0,0 +1,60 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.internal.statistics.validator + +import com.intellij.internal.statistic.eventLog.FeatureUsageData +import com.intellij.internal.statistic.eventLog.validator.ValidationResultType +import com.intellij.internal.statistic.eventLog.validator.rules.EventContext +import com.intellij.internal.statistic.eventLog.validator.rules.impl.CustomWhiteListRule +import com.intellij.internal.statistic.eventLog.validator.rules.impl.LocalFileCustomWhiteListRule +import com.intellij.testFramework.LightPlatformTestCase +import junit.framework.TestCase +import org.junit.Test + +class LocalFileWhitelistValidatorTest : LightPlatformTestCase() { + + private fun doValidateEventId(validator: CustomWhiteListRule, eventId: String, eventData: FeatureUsageData) { + val context = EventContext.create(eventId, eventData.build()) + doTest(ValidationResultType.ACCEPTED, validator, eventId, context) + } + + private fun doRejectEventId(validator: CustomWhiteListRule, eventId: String, eventData: FeatureUsageData) { + val context = EventContext.create(eventId, eventData.build()) + doTest(ValidationResultType.REJECTED, validator, eventId, context) + } + + private fun doTest(expected: ValidationResultType, validator: CustomWhiteListRule, data: String, context: EventContext) { + TestCase.assertEquals(expected, validator.validate(data, context)) + } + + @Test + fun `test validate first allowed value by file`() { + val validator = TestLocalFileWhitelistValidator() + doValidateEventId(validator, "allowed.value", FeatureUsageData()) + } + + @Test + fun `test validate second allowed value by file`() { + val validator = TestLocalFileWhitelistValidator() + doValidateEventId(validator, "another.allowed.value", FeatureUsageData()) + } + + @Test + fun `test reject unknown value`() { + val validator = TestLocalFileWhitelistValidator() + doRejectEventId(validator, "unknown.value", FeatureUsageData()) + } + + @Test + fun `test reject value if file doesn't exist`() { + val validator = EmptyLocalFileWhitelistValidator() + doRejectEventId(validator, "value", FeatureUsageData()) + } +} + +private class TestLocalFileWhitelistValidator : LocalFileCustomWhiteListRule("local_file", + LocalFileWhitelistValidatorTest::class.java, + "file-with-allowed-values.txt") + +private class EmptyLocalFileWhitelistValidator : LocalFileCustomWhiteListRule("local_file", + LocalFileWhitelistValidatorTest::class.java, + "not-existing-file.txt") \ No newline at end of file diff --git a/platform/platform-tests/testSrc/com/intellij/internal/statistics/validator/file-with-allowed-values.txt b/platform/platform-tests/testSrc/com/intellij/internal/statistics/validator/file-with-allowed-values.txt new file mode 100644 index 000000000000..d36a6cf25eaf --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/internal/statistics/validator/file-with-allowed-values.txt @@ -0,0 +1,2 @@ +allowed.value +another.allowed.value \ No newline at end of file