The PolymartPlatform class implements the Platform interface to upload plugin files to Polymart marketplace using their two-step upload API.
Package
me.hsgamer.mcreleaser.polymart.PolymartPlatform
Class Declaration
public class PolymartPlatform implements Platform {
private final Logger logger = LoggerFactory.getLogger(getClass());
}
Methods
createUploadRunnable
Creates a batch runnable that uploads files to Polymart.
The bundle of files to upload (only primary file is used)
Returns a BatchRunnable if all required properties are present, otherwise empty
@Override
public Optional<BatchRunnable> createUploadRunnable(FileBundle fileBundle) {
if (PropertyKeyUtil.isAbsentAndAnnounce(logger,
PolymartPropertyKey.KEY,
PolymartPropertyKey.RESOURCE)) {
return Optional.empty();
}
// Create upload tasks...
}
Upload Process
Polymart uses a two-step upload process executed in three phases:
Phase 0: Client Initialization
CloseableHttpClient client = HttpClients.createMinimal();
Phase 1: Update Preparation
Prepares the update metadata and converts Markdown to HTML:
Map<String, String> jsonBody = new HashMap<>();
jsonBody.put("api_key", PolymartPropertyKey.KEY.getValue());
jsonBody.put("product", PolymartPropertyKey.RESOURCE.getValue());
jsonBody.put("version", CommonPropertyKey.VERSION.getValue());
jsonBody.put("file_name", fileBundle.primaryFile().getName());
jsonBody.put("tag", PolymartPropertyKey.TAG.getValue("release"));
if (CommonPropertyKey.NAME.getValue() != null) {
jsonBody.put("update_title", CommonPropertyKey.NAME.getValue());
}
String description = CommonPropertyKey.DESCRIPTION.getValue();
String htmlDescription = MarkdownToHTMLConverter.convert(description);
jsonBody.put("update_description", htmlDescription);
Phase 2: Two-Step Upload
Step 1: Post Update
HttpPost request = new HttpPost("https://api.polymart.org/v1/doPostUpdate");
request.setEntity(new StringEntity(gson.toJson(jsonBody), ContentType.APPLICATION_JSON));
JsonObject responseNode = client.execute(request, response -> {
JsonObject responseBody = JsonParser.parseString(
EntityUtils.toString(response.getEntity())
).getAsJsonObject();
JsonObject uploadNode = responseBody.getAsJsonObject("response")
.getAsJsonObject("upload");
String uploadUrl = uploadNode.get("url").getAsString();
JsonObject fieldsNode = uploadNode.getAsJsonObject("fields");
return Map.of("uploadUrl", uploadUrl, "uploadFields", fieldsNode);
});
Step 2: Upload File
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
// Add all fields from the response
fieldsNode.entrySet().forEach(entry -> {
builder.addTextBody(entry.getKey(), entry.getValue().getAsString());
});
// Add the file
builder.addBinaryBody("file", fileBundle.primaryFile());
HttpPost uploadRequest = new HttpPost(uploadUrl);
uploadRequest.setEntity(builder.build());
Required Properties
Optional Properties
Update tag/type: “release”, “beta”, or “alpha”
Common Properties
Update description (Markdown, automatically converted to HTML)
Configuration Examples
Basic Plugin Update
export POLYMART_KEY="your-polymart-api-key"
export POLYMART_RESOURCE="12345"
export NAME="MyPlugin v1.0.0"
export VERSION="1.0.0"
export DESCRIPTION="## Features\n\n- New feature 1\n- Bug fix 2"
export FILES="build/libs/my-plugin.jar"
java -jar mcreleaser-cli.jar
Beta Release
export POLYMART_TAG="beta"
export VERSION="2.0.0-beta.1"
export DESCRIPTION="Beta release for testing"
Alpha Release
export POLYMART_TAG="alpha"
export VERSION="3.0.0-alpha.1"
Without Update Title
# NAME is optional - can be omitted
export VERSION="1.5.0"
export DESCRIPTION="Minor update"
Property Keys Source
Defined in PolymartPropertyKey:
public interface PolymartPropertyKey {
PropertyPrefix POLYMART = new PropertyPrefix("polymart");
PropertyKey KEY = POLYMART.key("key");
PropertyKey RESOURCE = POLYMART.key("resource");
PropertyKey TAG = POLYMART.key("tag");
}
Property Name Mapping:
- Property:
polymartKey → Environment: POLYMART_KEY
- Property:
polymartResource → Environment: POLYMART_RESOURCE
- Property:
polymartTag → Environment: POLYMART_TAG
Tag Values
release - Stable release (default)
beta - Beta version
alpha - Alpha/development version
Markdown to HTML Conversion
Polymart requires HTML descriptions. MCReleaser automatically converts Markdown:
String description = CommonPropertyKey.DESCRIPTION.getValue();
String htmlDescription = MarkdownToHTMLConverter.convert(description);
jsonBody.put("update_description", htmlDescription);
Example Conversion:
## New Features
- Feature 1
- Feature 2
**Important:** Breaking changes!
Converts to:
<h2>New Features</h2>
<ul>
<li>Feature 1</li>
<li>Feature 2</li>
</ul>
<p><strong>Important:</strong> Breaking changes!</p>
File Handling
Important: Polymart only uploads the primary file from the FileBundle:
jsonBody.put("file_name", fileBundle.primaryFile().getName());
// ...
builder.addBinaryBody("file", fileBundle.primaryFile());
Secondary files are ignored.
Two-Step Upload Flow
-
doPostUpdate: Registers the update and gets upload URL
- Returns signed upload URL
- Returns form fields required for upload
-
File Upload: Uploads file to the signed URL
- Uses multipart form data
- Includes all fields from step 1
- Adds the file
Error Handling
Post Update Failure
if (response.getCode() != 200) {
String responseBody = EntityUtils.toString(response.getEntity());
logger.error("Failed to post update: {} - {}",
response.getCode(), responseBody);
return false;
}
Markdown Conversion Failure
try {
String htmlDescription = MarkdownToHTMLConverter.convert(description);
jsonBody.put("update_description", htmlDescription);
} catch (Exception e) {
logger.error("Failed to convert description", e);
process.complete();
}
Upload Failure
if (response.getCode() != 200 && response.getCode() != 204) {
String responseBody = EntityUtils.toString(response.getEntity());
logger.error("Failed to upload file: {} - {}",
response.getCode(), responseBody);
return false;
}
API Endpoints
Post Update
POST https://api.polymart.org/v1/doPostUpdate
Content-Type: application/json
Request Body:
{
"api_key": "your-api-key",
"product": "12345",
"version": "1.0.0",
"file_name": "my-plugin.jar",
"tag": "release",
"update_title": "MyPlugin v1.0.0",
"update_description": "<p>HTML description</p>"
}
Response:
{
"response": {
"upload": {
"url": "https://upload.url",
"fields": {
"key": "value",
// ... other fields
}
}
}
}
File Upload
POST <url from previous response>
Content-Type: multipart/form-data
Includes all fields from the upload response plus the file.