Skip to content
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
5 changes: 5 additions & 0 deletions compatibility-tests/sdk-test-java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@
<groupId>software.amazon.awssdk</groupId>
<artifactId>marketplacecatalog</artifactId>
</dependency>
<!-- AWS Marketplace Agreement client -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>marketplaceagreement</artifactId>
</dependency>
<!-- IAM Access Analyzer client -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import software.amazon.awssdk.services.macie2.Macie2Client;
import software.amazon.awssdk.services.controlcatalog.ControlCatalogClient;
import software.amazon.awssdk.services.marketplacecatalog.MarketplaceCatalogClient;
import software.amazon.awssdk.services.marketplaceagreement.MarketplaceAgreementClient;
import software.amazon.awssdk.services.inspector2.Inspector2Client;
import software.amazon.awssdk.services.securityhub.SecurityHubClient;
import software.amazon.awssdk.services.detective.DetectiveClient;
Expand Down Expand Up @@ -368,6 +369,14 @@ public static MarketplaceCatalogClient marketplaceCatalogClient() {
.build();
}

public static MarketplaceAgreementClient marketplaceAgreementClient() {
return MarketplaceAgreementClient.builder()
.endpointOverride(ENDPOINT)
.region(REGION)
.credentialsProvider(CREDENTIALS)
.build();
}

public static DetectiveClient detectiveClient(String accountId) {
return DetectiveClient.builder()
.endpointOverride(ENDPOINT)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.floci.test;

import org.junit.jupiter.api.Test;
import software.amazon.awssdk.services.marketplaceagreement.MarketplaceAgreementClient;

import static org.junit.jupiter.api.Assertions.assertNotNull;

class MarketplaceAgreementTest {

@Test
void usesAwsSdkWireContract() {
try (MarketplaceAgreementClient client = TestFixtures.marketplaceAgreementClient()) {
var response = client.searchAgreements(r -> r.filters(
f -> f.name("AgreementType").values("PurchaseAgreement")));
assertNotNull(response.agreementViewSummaries());
}
}
}
38 changes: 37 additions & 1 deletion docs/services/marketplace.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Floci emulates AWS Marketplace APIs under the shared `aws-marketplace` SigV4 signing scope.

## Marketplace Catalog
## Supported operations

<!-- floci:actions:start -->
| Action | Description |
Expand All @@ -22,10 +22,46 @@ Floci emulates AWS Marketplace APIs under the shared `aws-marketplace` SigV4 sig
| `StartChangeSet` | Starts an idempotent Marketplace Catalog change set |
| `TagResource` | Adds or replaces tags on a Marketplace Catalog resource |
| `UntagResource` | Removes tag keys from a Marketplace Catalog resource |
| `AcceptAgreementCancellationRequest` | Accepts an agreement cancellation request |
| `AcceptAgreementPaymentRequest` | Accepts an agreement payment request |
| `AcceptAgreementRequest` | Accepts an agreement request |
| `BatchCreateBillingAdjustmentRequest` | Creates billing adjustment requests in a batch |
| `CancelAgreement` | Cancels an agreement |
| `CancelAgreementCancellationRequest` | Cancels an agreement cancellation request |
| `CancelAgreementPaymentRequest` | Cancels an agreement payment request |
| `CreateAgreementRequest` | Creates an agreement request |
| `DescribeAgreement` | Describes an agreement |
| `GetAgreementCancellationRequest` | Gets an agreement cancellation request |
| `GetAgreementEntitlements` | Gets entitlements for an agreement |
| `GetAgreementPaymentRequest` | Gets an agreement payment request |
| `GetAgreementTerms` | Gets accepted agreement terms |
| `GetBillingAdjustmentRequest` | Gets a billing adjustment request |
| `ListAgreementCancellationRequests` | Lists agreement cancellation requests |
| `ListAgreementCharges` | Lists agreement charges |
| `ListAgreementInvoiceLineItems` | Lists agreement invoice line items |
| `ListAgreementPaymentRequests` | Lists agreement payment requests |
| `ListBillingAdjustmentRequests` | Lists billing adjustment requests |
| `RejectAgreementCancellationRequest` | Rejects an agreement cancellation request |
| `RejectAgreementPaymentRequest` | Rejects an agreement payment request |
| `SearchAgreements` | Searches agreements |
| `SendAgreementCancellationRequest` | Sends an agreement cancellation request |
| `SendAgreementPaymentRequest` | Sends an agreement payment request |
| `UpdatePurchaseOrders` | Updates purchase orders for an agreement |
<!-- floci:actions:end -->

## Marketplace Catalog

Catalog change sets use AWS states (`PREPARING`, `APPLYING`, `SUCCEEDED`, and `CANCELLED`). Floci applies supported entity mutations locally when a change set is observed and persists entities, change sets, tags, resource policies, and assessments through `StorageFactory`, isolated by AWS account.

## Marketplace Agreement

Agreement request acceptance persists the resulting agreement and exposes it through subsequent read and search operations. State is isolated by AWS account through `StorageFactory`.

### Known deviations

- Accepted agreements use a local 365-day duration instead of deriving the end time from requested terms.
- `PartyType` is validated for AWS-compatible values but does not otherwise change which locally stored agreements match.

## Configuration

| Variable | Default | Description |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.github.hectorvent.floci.services.dynamodb.DynamoDbResponses;
import io.github.hectorvent.floci.services.dynamodb.DynamoDbStreamsJsonHandler;
import io.github.hectorvent.floci.services.networkfirewall.NetworkFirewallJsonHandler;
import io.github.hectorvent.floci.services.marketplace.MarketplaceJsonHandler;
import io.github.hectorvent.floci.services.sns.SnsJsonHandler;
import io.github.hectorvent.floci.services.sqs.SqsJsonHandler;
import io.github.hectorvent.floci.services.stepfunctions.StepFunctionsJsonHandler;
Expand Down Expand Up @@ -49,6 +50,7 @@ public class AwsJsonController {
private final CloudControlJsonHandler cloudControlJsonHandler;
private final SwfJsonHandler swfJsonHandler;
private final NetworkFirewallJsonHandler networkFirewallJsonHandler;
private final MarketplaceJsonHandler marketplaceJsonHandler;

@Inject
public AwsJsonController(ObjectMapper objectMapper, ResolvedServiceCatalog catalog,
Expand All @@ -60,7 +62,8 @@ public AwsJsonController(ObjectMapper objectMapper, ResolvedServiceCatalog catal
CloudWatchMetricsJsonHandler cloudWatchMetricsJsonHandler,
CloudControlJsonHandler cloudControlJsonHandler,
SwfJsonHandler swfJsonHandler,
NetworkFirewallJsonHandler networkFirewallJsonHandler) {
NetworkFirewallJsonHandler networkFirewallJsonHandler,
MarketplaceJsonHandler marketplaceJsonHandler) {
this.objectMapper = objectMapper;
this.strictBodyReader = objectMapper.reader().with(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
this.catalog = catalog;
Expand All @@ -74,6 +77,7 @@ public AwsJsonController(ObjectMapper objectMapper, ResolvedServiceCatalog catal
this.cloudControlJsonHandler = cloudControlJsonHandler;
this.swfJsonHandler = swfJsonHandler;
this.networkFirewallJsonHandler = networkFirewallJsonHandler;
this.marketplaceJsonHandler = marketplaceJsonHandler;
}

@POST
Expand Down Expand Up @@ -124,6 +128,7 @@ public Response handleJsonRequest(
action, request, region, regionResolver.getAccountId());
case "network-firewall" -> networkFirewallJsonHandler.handle(
action, request, region, regionResolver.getAccountId());
case "marketplace" -> marketplaceJsonHandler.handle(action, request, region);
default -> null;
};
// catalog.matchTarget is protocol-agnostic: a JSON 1.1 target
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -644,8 +644,8 @@ public ResolvedServiceCatalog(EmulatorConfig config) {
Set.of(io.github.hectorvent.floci.services.codegurureviewer.CodeGuruReviewerController.class)),
descriptor("marketplace", "marketplace", config.services().marketplace().enabled(), true,
"marketplace", config.storage().mode(), 5000L, null, ServiceProtocol.REST_JSON,
protocols(ServiceProtocol.REST_JSON),
Set.of(), Set.of("aws-marketplace"), Set.of(),
protocols(ServiceProtocol.REST_JSON, ServiceProtocol.JSON),
Set.of("AWSMPCommerceService_v20200301."), Set.of("aws-marketplace"), Set.of(),
Set.of(MarketplaceCatalogController.class))
));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.github.hectorvent.floci.services.marketplace;

import com.fasterxml.jackson.databind.JsonNode;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

/** Service-specific controller invoked by the shared AWS JSON 1.0 protocol handler. */
@ApplicationScoped
public class MarketplaceAgreementController {
private final MarketplaceAgreementService service;

@Inject
public MarketplaceAgreementController(MarketplaceAgreementService service) {
this.service = service;
}

public JsonNode handle(String action, JsonNode request, String region) {
return service.handle(action, request, region);
}
}
Loading
Loading