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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ PassninjaResponse<Pass> response = Pass.create("demo.coupon", /* passType */
pass /* passData */);

System.out.println(response.getResponseBody().getUrls());
System.out.println(response.getResponseBody().getPassType());
System.out.println(response.getResponseBody().getPassTemplate());
System.out.println(response.getResponseBody().getSerialNumber());
```

Expand Down
27 changes: 20 additions & 7 deletions src/main/java/com/passninja/model/Pass.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,27 @@ public class Pass extends ApiResource {
public static final String RESOURCE = "passes";

@JsonProperty private final String id;
@JsonProperty private final String passType;
@JsonProperty private final String passTemplate;
@JsonProperty private final String serialNumber;
@JsonProperty private final Map<String, Object> pass;
@JsonProperty private final Map<String, Object> urls;

@JsonCreator
public Pass(
@JsonProperty("id") final String id,
@JsonProperty("passType") final String passType,
@JsonProperty("passTemplate") final String passTemplate,
@JsonProperty("serialNumber") final String serialNumber,
@JsonProperty("pass") final Map<String, Object> pass,
@JsonProperty("urls") final Map<String, Object> urls) {
this.id = id;
this.passType = passType;
this.passTemplate = passTemplate;
this.serialNumber = serialNumber;
this.pass = pass;
this.urls = urls;
}

public String getPassType() {
return passType;
public String getPassTemplate() {
return passTemplate;
}

public String getSerialNumber() {
Expand All @@ -56,7 +56,7 @@ public Map<String, Object> getUrls() {
@Override
public String toString() {
return "Pass{"
+ "passType='" + passType + '\''
+ "passTemplate='" + passTemplate + '\''
+ ", serialNumber='" + serialNumber + '\''
+ ", pass='" + pass + '\''
+ ", urls='" + urls + '\''
Expand All @@ -71,7 +71,7 @@ public RequestBuilder() {
}

public RequestBuilder setPassType(String passType) {
params.put("passType", passType);
params.put("passTemplate", passType);
return this;
}

Expand Down Expand Up @@ -121,6 +121,19 @@ public static PassninjaResponse<Pass> put(String passType, String serialNumber,
return request(RequestMethod.PUT, RESOURCE + "/" + passType + "/" + serialNumber, params, Pass.class, null);
}

// Partial update: only the provided fields change (PUT is a full replace that
// requires all required fields). HttpURLConnection cannot issue PATCH, so this
// sends a POST with the server's `_method=PATCH` override (POST-only).
public static PassninjaResponse<Pass> patch(String passType, String serialNumber, Map<String, Object> pass)
throws ApiException, IOException, AuthenticationException {
Map<String, Object> params = new HashMap<>();
params.put("pass", pass);
Map<String, Object> query = new HashMap<>();
query.put("_method", "PATCH");
return request(RequestMethod.POST, RESOURCE + "/" + passType + "/" + serialNumber, params,
query, Pass.class, null);
}

public static PassninjaResponse<Pass> delete(String passType, String serialNumber) throws ApiException, IOException,
AuthenticationException {
return request(RequestMethod.DELETE, RESOURCE + "/" + passType + "/" + serialNumber, null, Pass.class, null);
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/com/passninja/net/ResponseGetter.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,9 @@ private static <T> PassninjaResponse<T> requestInternal(ApiResource.RequestMetho
.map(item -> urlEncodePair(item.getKey(),
String.valueOf(item.getValue()))).collect(Collectors.joining());

String passninjaUrl = String.format("%s%s", Passninja.API_BASE_URL,
String apiBase = System.getProperty("passninja.apiBaseUrl",
Passninja.API_BASE_URL);
String passninjaUrl = String.format("%s%s", apiBase,
queryParams.isEmpty() ? url : url + "?" + queryParams);

String encodedData = createQuery(data);
Expand Down
74 changes: 74 additions & 0 deletions src/test/java/com/passninja/MockApiServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.passninja;

import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

/**
* Minimal in-process HTTP server used by the tests so the suite runs offline
* (the JDK ships com.sun.net.httpserver, so no extra dependency is needed).
* Routes the handful of endpoints the client exercises to canned JSON.
*/
public class MockApiServer {

private static final String PASS_JSON =
"{\"id\":\"serial123\",\"passTemplate\":\"ptk_0x2\",\"serialNumber\":\"serial123\","
+ "\"urls\":{\"landing\":\"https://i.installpass.es/p/serial123\"}}";
private static final String PASSES_JSON = "{\"passes\":[" + PASS_JSON + "]}";
private static final String DECRYPT_JSON = "{\"decrypted\":\"founder-id:abc123\"}";
private static final String TEMPLATE_JSON =
"{\"id\":\"ptk_0x2\",\"name\":\"Starbucks Rewards\",\"pass_type_id\":\"pass.com.example\","
+ "\"platform\":\"apple\",\"style\":\"storeCard\",\"issued_pass_count\":0,"
+ "\"installed_pass_count\":0}";

private HttpServer server;

/**
* Starts the server on an ephemeral port.
*
* @return the base URL (with trailing slash) to assign to Passninja.API_BASE_URL
* @throws IOException if the server cannot bind
*/
public String start() throws IOException {
server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/", exchange -> {
String path = exchange.getRequestURI().getPath();
String method = exchange.getRequestMethod();
int code = 200;
String body;
if (method.equals("POST") && path.equals("/v1/passes")) {
body = PASS_JSON;
} else if (method.equals("POST") && path.matches("/v1/passes/[^/]+/decrypt")) {
body = DECRYPT_JSON;
} else if (method.equals("GET") && path.matches("/v1/passes/[^/]+/[^/]+")) {
body = PASS_JSON;
} else if (method.equals("PUT") && path.matches("/v1/passes/[^/]+/[^/]+")) {
body = PASS_JSON;
} else if (method.equals("GET") && path.matches("/v1/passes/[^/]+")) {
body = PASSES_JSON;
} else if (method.equals("GET") && path.matches("/v1/pass_templates/[^/]+")) {
body = TEMPLATE_JSON;
} else {
code = 404;
body = "{\"error\":\"not found: " + method + " " + path + "\"}";
}
byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "application/json");
exchange.sendResponseHeaders(code, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
}
});
server.start();
return "http://127.0.0.1:" + server.getAddress().getPort() + "/v1/";
}

public void stop() {
if (server != null) {
server.stop(0);
}
}
}
14 changes: 13 additions & 1 deletion src/test/java/com/passninja/model/PassTemplateTest.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.passninja.model;

import com.passninja.MockApiServer;
import com.passninja.Passninja;
import com.passninja.net.PassninjaResponse;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
Expand All @@ -12,11 +14,21 @@
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class PassTemplateTest {

private MockApiServer mock;

@BeforeEach
void setup() {
void setup() throws Exception {
mock = new MockApiServer();
System.setProperty("passninja.apiBaseUrl", mock.start());
Passninja.init("aid_0x2", "d5247644c316194d9089e23766e08ea9");
}

@AfterEach
void teardown() {
mock.stop();
System.clearProperty("passninja.apiBaseUrl");
}

@Test
void find_pass_template() throws Exception {
PassninjaResponse<PassTemplate> response = PassTemplate.find("ptk_0x2");
Expand Down
13 changes: 12 additions & 1 deletion src/test/java/com/passninja/model/PassTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.passninja.model;

import com.passninja.MockApiServer;
import com.passninja.Passninja;
import com.passninja.exception.AuthenticationException;
import com.passninja.net.PassninjaResponse;
Expand All @@ -14,11 +15,21 @@
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
public class PassTest {

private MockApiServer mock;

@BeforeEach
void setup() {
void setup() throws Exception {
mock = new MockApiServer();
System.setProperty("passninja.apiBaseUrl", mock.start());
Passninja.init("aid_0x2", "d5247644c316194d9089e23766e08ea9");
}

@AfterEach
void teardown() {
mock.stop();
System.clearProperty("passninja.apiBaseUrl");
}

@Test
public void should_create_a_new_pass() throws Exception {
Map<String, Object> inputPass = new HashMap<>();
Expand Down
Loading