Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transforms to remove deprecated shapes #2452

Merged
merged 3 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ sphinx-tabs>=3.4.4

sphinx_copybutton==0.5.0
sphinx-autobuild>=2024.4.16

# needs to be added as it is removed in python3.13
standard-imghdr==3.13.0
57 changes: 57 additions & 0 deletions docs/source-2.0/guides/smithy-build-json.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,61 @@ but keeps the shape if it has any of the provided tags:
}
}

.. _removeDeprecatedShapes-transform:

removeDeprecatedShapes
-----------------------

Removes any shapes that were marked as ``@deprecated`` before the specified version or date.

This transform can be used to filter out shapes with the :ref:`deprecated trait <deprecated-trait>` applied if the
``since`` property of the trait specifies a version or date. Versions are expected to follow the
`SemVer Specification`_ and dates are expected to be `ISO 8601`_ calendar dates (YYYY-MM-DD).
If the value of the ``since`` property is not a valid SemVer version or ISO 8601 calendar date then the
``@deprecated`` trait will be ignored by this transform.

If the version or date in the ``since`` property of a ``@deprecated`` trait is before the configured version or date, then
the shape the ``@deprecated`` trait is applied to will be removed from the model. Shapes with the ``@deprecated`` trait,
but no ``since`` property will be ignored by this transform.

.. list-table::
:header-rows: 1
:widths: 10 20 70

* - Property
- Type
- Description
* - relativeDate
- ``string``
- Date, in `ISO 8601`_ calendar date format (YYYY-MM-DD), to use to filter out deprecated shapes.
Any shapes deprecated before this date will be removed from the model.
* - relativeVersion
- ``string``
- Version, following the `SemVer Specification`_, to use to filter out deprecated shapes.
Any shapes deprecated in an earlier version will be removed from the model.

The following example removes any deprecated shapes that were deprecated before ``2024-10-10``
or before version ``1.1.0``.

.. code-block:: json

{
"version": "1.0",
"projections": {
"exampleProjection": {
"transforms": [
{
"name": "removeDeprecatedShapes",
"args": {
"relativeDate": "2024-10-10",
"relativeVersion": "1.1.0"
}
}
]
}
}
}

.. _renameShapes-transform:

renameShapes
Expand Down Expand Up @@ -1964,3 +2019,5 @@ Assuming ``hello.sh`` is on the PATH and might look something like:
.. _Apache Maven: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
.. _Maven Central: https://search.maven.org
.. _official Maven documentation: https://maven.apache.org/pom.html#dependency-version-requirement-specification
.. _SemVer Specification: https://semver.org/
.. _ISO 8601: https://www.iso.org/iso-8601-date-and-time-format.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.build.transforms;

import software.amazon.smithy.build.TransformContext;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.transform.ModelTransformer;

/**
* {@code removeDeprecatedShapes} removes shapes from a model if they have been deprecated before a version or date.
*/
public final class RemoveDeprecatedShapes extends ConfigurableProjectionTransformer<RemoveDeprecatedShapes.Config> {

/**
* {@code RemoveDeprecatedShapes} configuration settings.
*/
public static final class Config {
private String relativeDate;
private String relativeVersion;

/**
* Gets the date used to filter deprecated shapes.
*
* @return The date used to filter deprecated shapes.
*/
public String getRelativeDate() {
return relativeDate;
}

/**
* Sets the date used to filter deprecated shapes.
*
* @param relativeDate The date used to filter deprecated shapes.
*/
public void setRelativeDate(String relativeDate) {
this.relativeDate = relativeDate;
}

/**
* Gets the version used to filter deprecated shapes.
*
* @return The version used to filter deprecated shapes.
*/
public String getRelativeVersion() {
return relativeVersion;
}

/**
* Sets the version used to filter deprecated shapes.
*
* @param relativeVersion The version used to filter deprecated shapes.
*/
public void setRelativeVersion(String relativeVersion) {
this.relativeVersion = relativeVersion;
}
}

@Override
public String getName() {
return "removeDeprecatedShapes";
}

@Override
public Class<Config> getConfigType() {
return Config.class;
}

@Override
protected Model transformWithConfig(TransformContext context, Config config) {
Model model = context.getModel();
ModelTransformer transformer = context.getTransformer();
model = transformer.filterDeprecatedRelativeDate(model, config.getRelativeDate());
model = transformer.filterDeprecatedRelativeVersion(model, config.getRelativeVersion());
return model;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.build.transforms;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.nio.file.Paths;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.build.TransformContext;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.shapes.ShapeId;

public class RemoveDeprecatedShapesTest {
@Test
public void removesAllDeprecatedShapes() throws Exception {
Model model = Model.assembler()
.addImport(Paths.get(getClass().getResource("remove-deprecated.smithy").toURI()))
.assemble()
.unwrap();
TransformContext context = TransformContext.builder()
.model(model)
.settings(Node.objectNode()
.withMember("relativeDate", Node.from("2024-10-10"))
.withMember("relativeVersion", Node.from("1.1.0")))
.build();
Model result = new RemoveDeprecatedShapes().transform(context);

// Deprecated by date removed
assertFalse(result.getShape(ShapeId.from("smithy.example#FilteredBeforeHyphens")).isPresent());
assertFalse(result.getShape(ShapeId.from("smithy.example#FilteredVersionBefore")).isPresent());

// Equal to the filter retained
assertTrue(result.getShape(ShapeId.from("smithy.example#NotFilteredDateEquals")).isPresent());
assertTrue(result.getShape(ShapeId.from("smithy.example#NotFilteredVersionEquals")).isPresent());

// After filter retained
assertTrue(result.getShape(ShapeId.from("smithy.example#NotFilteredDateAfter")).isPresent());
assertTrue(result.getShape(ShapeId.from("smithy.example#NotFilteredVersionAfter")).isPresent());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
$version: "2.0"

namespace smithy.example

@deprecated(message: "Should be filtered as it is deprecated before date", since: "2024-10-09")
structure FilteredDateBefore {
field: String
}

@deprecated(message: "Should NOT be filtered as it is deprecated at the same date as the filter", since: "2024-10-10")
structure NotFilteredDateEquals {
field: String
}

@deprecated(message: "Should NOT be filtered as it is deprecated after the filter date", since: "2024-10-11")
structure NotFilteredDateAfter {
field: String
}

@deprecated(message: "Should be filtered as it is deprecated before version", since: "1.0.0")
structure FilteredVersionBefore {
field: String
}

@deprecated(message: "Should NOT be filtered as it is deprecated at the same version as the filter", since: "1.1.0")
structure NotFilteredVersionEquals {
field: String
}

@deprecated(message: "Should NOT be filtered as it is deprecated after the filter version", since: "1.1.1")
structure NotFilteredVersionAfter {
field: String
}
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,32 @@ public void createDedicatedInputsAndOutputs(String inputSuffix, String outputSuf
});
}

/**
* Removes any shapes deprecated before the specified date.
*
* @param relativeDate Relative date, in YYYY-MM-DD format, to use to filter out deprecated shapes.
* @see ModelTransformer#filterDeprecatedRelativeDate(Model, String)
*/
public void removeShapesDeprecatedBeforeDate(String relativeDate) {
transforms.add((model, transformer) -> {
LOGGER.finest("Removing shapes deprecated before date: " + relativeDate);
return transformer.filterDeprecatedRelativeDate(model, relativeDate);
});
}

/**
* Removes any shapes deprecated before the specified version.
*
* @param relativeVersion Version, in SemVer format, to use to filter out deprecated shapes.
* @see ModelTransformer#filterDeprecatedRelativeVersion(Model, String)
*/
public void removeShapesDeprecatedBeforeVersion(String relativeVersion) {
transforms.add((model, transformer) -> {
LOGGER.finest("Removing shapes deprecated before version: " + relativeVersion);
return transformer.filterDeprecatedRelativeVersion(model, relativeVersion);
});
}

/**
* Changes each compatible string shape with the enum trait to an enum shape.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.model.transform;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.traits.DeprecatedTrait;

final class FilterDeprecatedRelativeDate {
private static final Logger LOGGER = Logger.getLogger(FilterDeprecatedRelativeDate.class.getName());
// YYYY-MM-DD calendar date with optional hyphens.
private static final Pattern ISO_8601_DATE_REGEX = Pattern.compile(
"^([0-9]{4})-?(1[0-2]|0[1-9])-?(3[01]|0[1-9]|[12][0-9])$"
);

private final String relativeDate;

FilterDeprecatedRelativeDate(String relativeDate) {
if (relativeDate != null && !isIso8601Date(relativeDate)) {
throw new IllegalArgumentException("Provided relativeDate: `"
+ relativeDate
+ "` does not match ISO8601 calendar date format (YYYY-MM-DD)."
);
}
this.relativeDate = relativeDate != null ? relativeDate.replace("-", "") : null;
}

Model transform(ModelTransformer transformer, Model model) {
// If there is no filter. Exit without traversing shapes
if (relativeDate == null) {
return model;
}

Set<Shape> shapesToRemove = new HashSet<>();
for (Shape shape : model.getShapesWithTrait(DeprecatedTrait.class)) {
Optional<String> sinceOptional = shape.expectTrait(DeprecatedTrait.class).getSince();
if (!sinceOptional.isPresent()) {
continue;
}
String since = sinceOptional.get();

if (isIso8601Date(since)) {
// Compare lexicographical ordering without hyphens.
if (relativeDate.compareTo(since.replace("-", "")) > 0) {
LOGGER.fine("Filtering deprecated shape: `"
+ shape + "`"
+ ". Shape was deprecated as of: " + since
);
shapesToRemove.add(shape);
}
}
}

return transformer.removeShapes(model, shapesToRemove);
}

private static boolean isIso8601Date(String string) {
return ISO_8601_DATE_REGEX.matcher(string).matches();
}
}
Loading
Loading