Skip to content

Commit

Permalink
Add recipe to change variation default (#16)
Browse files Browse the repository at this point in the history
* Add recipe to change variation default

Fixes #13

* No need for else when using return statement

* Add package-info.java to reduce warnings

* Apply suggestions from code review

Co-authored-by: Shannon Pamperl <[email protected]>

* Use `Tree.randomId()` and retain prefix

---------

Co-authored-by: Shannon Pamperl <[email protected]>
  • Loading branch information
timtebeek and shanman190 authored Dec 2, 2023
1 parent 9b758e3 commit d3136c8
Show file tree
Hide file tree
Showing 4 changed files with 376 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.launchdarkly;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.*;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.internal.lang.NonNull;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.Space;
import org.openrewrite.marker.Markers;

@Value
@EqualsAndHashCode(callSuper = false)
public class ChangeVariationDefault extends Recipe {
@Override
public String getDisplayName() {
return "Change the default value for feature key";
}

@Override
public String getDescription() {
return "Change the default value for `Variation` invocations for feature key.";
}

@Option(displayName = "Feature flag key",
description = "The key of the feature flag to remove.",
example = "flag-key-123abc")
@NonNull
String featureKey;

@Option(displayName = "Default value",
description = "The default value to use in feature flag invocations.",
example = "true")
@NonNull
String defaultValue;

private static final MethodMatcher BOOL_VARIATION_MATCHER = new MethodMatcher("com.launchdarkly.sdk.server.LDClient boolVariation(String, com.launchdarkly.sdk.*, boolean)", true);
private static final MethodMatcher STRING_VARIATION_MATCHER = new MethodMatcher("com.launchdarkly.sdk.server.LDClient stringVariation(String, com.launchdarkly.sdk.*, String)", true);
private static final MethodMatcher INT_VARIATION_MATCHER = new MethodMatcher("com.launchdarkly.sdk.server.LDClient intVariation(String, com.launchdarkly.sdk.*, int)", true);
private static final MethodMatcher DOUBLE_VARIATION_MATCHER = new MethodMatcher("com.launchdarkly.sdk.server.LDClient doubleVariation(String, com.launchdarkly.sdk.*, double)", true);
// Not yet handling JSON_VARIATION_MATCHER, as that takes a `com.launchdarkly.sdk.LDValue` argument

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
JavaIsoVisitor<ExecutionContext> visitor = new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) {
J.MethodInvocation mi = super.visitMethodInvocation(method, executionContext);
if (BOOL_VARIATION_MATCHER.matches(mi) && J.Literal.isLiteralValue(mi.getArguments().get(0), featureKey)) {
J.Literal literal = new J.Literal(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, defaultValue, defaultValue, null, JavaType.Primitive.Boolean);
return mi.withArguments(ListUtils.mapLast(mi.getArguments(), a -> literal.withPrefix(a.getPrefix())));
}
if (STRING_VARIATION_MATCHER.matches(mi) && J.Literal.isLiteralValue(mi.getArguments().get(0), featureKey)) {
J.Literal literal = new J.Literal(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, defaultValue, "\"" + defaultValue + "\"", null, JavaType.Primitive.String);
return mi.withArguments(ListUtils.mapLast(mi.getArguments(), a -> literal.withPrefix(a.getPrefix())));
}
if (INT_VARIATION_MATCHER.matches(mi) && J.Literal.isLiteralValue(mi.getArguments().get(0), featureKey)) {
J.Literal literal = new J.Literal(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, defaultValue, defaultValue, null, JavaType.Primitive.Int);
return mi.withArguments(ListUtils.mapLast(mi.getArguments(), a -> literal.withPrefix(a.getPrefix())));
}
if (DOUBLE_VARIATION_MATCHER.matches(mi) && J.Literal.isLiteralValue(mi.getArguments().get(0), featureKey)) {
J.Literal literal = new J.Literal(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, defaultValue, defaultValue, null, JavaType.Primitive.Double);
return mi.withArguments(ListUtils.mapLast(mi.getArguments(), a -> literal.withPrefix(a.getPrefix())));
}
return mi;
}
};
return Preconditions.check(
Preconditions.or(
new UsesMethod<>(BOOL_VARIATION_MATCHER),
new UsesMethod<>(STRING_VARIATION_MATCHER),
new UsesMethod<>(INT_VARIATION_MATCHER),
new UsesMethod<>(DOUBLE_VARIATION_MATCHER)),
visitor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
import org.openrewrite.staticanalysis.RemoveUnusedPrivateFields;
import org.openrewrite.staticanalysis.SimplifyConstantIfBranchExecution;

import java.util.UUID;

@Value
@EqualsAndHashCode(callSuper = false)
public class RemoveBoolVariation extends Recipe {
Expand Down Expand Up @@ -69,7 +67,7 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext execu
doAfterVisit(new SimplifyConstantIfBranchExecution().getVisitor());
doAfterVisit(new RemoveUnusedLocalVariables(null).getVisitor());
doAfterVisit(new RemoveUnusedPrivateFields().getVisitor());
return new J.Literal(UUID.randomUUID(), Space.SINGLE_SPACE, Markers.EMPTY, replacementValue, String.valueOf(replacementValue), null, JavaType.Primitive.Boolean);
return new J.Literal(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, replacementValue, String.valueOf(replacementValue), null, JavaType.Primitive.Boolean);
}
return mi;
}
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/org/openrewrite/launchdarkly/package-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NonNullApi
@NonNullFields
package org.openrewrite.launchdarkly;

import org.openrewrite.internal.lang.NonNullApi;
import org.openrewrite.internal.lang.NonNullFields;
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.launchdarkly;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

class ChangeVariationDefaultTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec.recipe(new ChangeVariationDefault("flag-key-123abc", "true"))
.parser(JavaParser.fromJavaVersion()
.classpathFromResources(new InMemoryExecutionContext(), "launchdarkly-java-server-sdk-6"));
}

@Nested
class BooleanVariation {
@Test
@DocumentExample
void changeDefaultValueToTrue() {
rewriteRun(
// language=java
java(
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
if (client.boolVariation("flag-key-123abc", context, false)) {
System.out.println("Feature is on");
}
}
}
""",
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
if (client.boolVariation("flag-key-123abc", context, true)) {
System.out.println("Feature is on");
}
}
}
"""
)
);
}

@Test
void changeDefaultValueToTrueEvenIfVariable() {
rewriteRun(
// language=java
java(
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
boolean defaultValue = false; // Not cleaned up
if (client.boolVariation("flag-key-123abc", context, defaultValue)) {
System.out.println("Feature is on");
}
}
}
""",
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
boolean defaultValue = false; // Not cleaned up
if (client.boolVariation("flag-key-123abc", context, true)) {
System.out.println("Feature is on");
}
}
}
"""
)
);
}

@Test
void changeDefaultValueToTrueEvenIfVariableWithComment() {
rewriteRun(
// language=java
java(
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
boolean defaultValue = false; // Not cleaned up
if (client.boolVariation("flag-key-123abc", context,
/* Retained */
defaultValue)) {
System.out.println("Feature is on");
}
}
}
""",
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
boolean defaultValue = false; // Not cleaned up
if (client.boolVariation("flag-key-123abc", context,
/* Retained */
true)) {
System.out.println("Feature is on");
}
}
}
"""
)
);
}
}

@Nested
class StringVariation {
@Test
@DocumentExample
void changeDefaultValueToTrue() {
rewriteRun(
// language=java
java(
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
if (client.stringVariation("flag-key-123abc", context, "foo")) {
System.out.println("Feature is on");
}
}
}
""",
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
if (client.stringVariation("flag-key-123abc", context, "true")) {
System.out.println("Feature is on");
}
}
}
"""
)
);
}
}

@Nested
class DoubleVariation {
@Test
@DocumentExample
void changeDefaultValueTo456() {
rewriteRun(
spec -> spec.recipe(new ChangeVariationDefault("flag-key-123abc", "4.56")),
// language=java
java(
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
if (client.doubleVariation("flag-key-123abc", context, 1.23)) {
System.out.println("Feature is on");
}
}
}
""",
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
if (client.doubleVariation("flag-key-123abc", context, 4.56)) {
System.out.println("Feature is on");
}
}
}
"""
)
);
}
}

@Nested
class IntVariation {
@Test
@DocumentExample
void changeDefaultValueTo456() {
rewriteRun(
spec -> spec.recipe(new ChangeVariationDefault("flag-key-123abc", "456")),
// language=java
java(
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
if (client.intVariation("flag-key-123abc", context, 123)) {
System.out.println("Feature is on");
}
}
}
""",
"""
import com.launchdarkly.sdk.LDContext;
import com.launchdarkly.sdk.server.LDClient;
class Foo {
private LDClient client = new LDClient("sdk-key-123abc");
void bar(LDContext context) {
if (client.intVariation("flag-key-123abc", context, 456)) {
System.out.println("Feature is on");
}
}
}
"""
)
);
}
}
}

0 comments on commit d3136c8

Please sign in to comment.