mirror of
https://github.com/LukeHagar/plexjava.git
synced 2025-12-06 04:20:46 +00:00
ci: regenerated with OpenAPI Doc , Speakeasy CLI 1.531.4
This commit is contained in:
@@ -1,19 +1,19 @@
|
|||||||
lockVersion: 2.0.0
|
lockVersion: 2.0.0
|
||||||
id: 1732900d-e173-47c1-a90d-d45182eb35d9
|
id: 1732900d-e173-47c1-a90d-d45182eb35d9
|
||||||
management:
|
management:
|
||||||
docChecksum: 184db864cffee563d43aae06915b9671
|
docChecksum: fa4c9c5c23680ad02fdbe831ad9d2403
|
||||||
docVersion: 0.0.3
|
docVersion: 0.0.3
|
||||||
speakeasyVersion: 1.529.1
|
speakeasyVersion: 1.531.4
|
||||||
generationVersion: 2.566.5
|
generationVersion: 2.570.4
|
||||||
releaseVersion: 0.14.2
|
releaseVersion: 0.15.0
|
||||||
configChecksum: bad4d944912dc794f8497b0cfca8b8b2
|
configChecksum: 8a775baa0e7c2ba4c10a7c8bb26dbcd3
|
||||||
repoURL: https://github.com/LukeHagar/plexjava.git
|
repoURL: https://github.com/LukeHagar/plexjava.git
|
||||||
published: true
|
published: true
|
||||||
features:
|
features:
|
||||||
java:
|
java:
|
||||||
additionalDependencies: 0.1.0
|
additionalDependencies: 0.1.0
|
||||||
constsAndDefaults: 0.1.1
|
constsAndDefaults: 0.1.1
|
||||||
core: 3.36.2
|
core: 3.37.1
|
||||||
deprecations: 2.81.1
|
deprecations: 2.81.1
|
||||||
downloadStreams: 0.1.1
|
downloadStreams: 0.1.1
|
||||||
enums: 2.81.2
|
enums: 2.81.2
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ generation:
|
|||||||
oAuth2ClientCredentialsEnabled: true
|
oAuth2ClientCredentialsEnabled: true
|
||||||
oAuth2PasswordEnabled: false
|
oAuth2PasswordEnabled: false
|
||||||
java:
|
java:
|
||||||
version: 0.14.2
|
version: 0.15.0
|
||||||
additionalDependencies: []
|
additionalDependencies: []
|
||||||
additionalPlugins: []
|
additionalPlugins: []
|
||||||
artifactID: plexapi
|
artifactID: plexapi
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
speakeasyVersion: 1.529.1
|
speakeasyVersion: 1.531.4
|
||||||
sources:
|
sources:
|
||||||
my-source:
|
my-source:
|
||||||
sourceNamespace: my-source
|
sourceNamespace: my-source
|
||||||
@@ -8,19 +8,19 @@ sources:
|
|||||||
- latest
|
- latest
|
||||||
plexapi:
|
plexapi:
|
||||||
sourceNamespace: plexapi
|
sourceNamespace: plexapi
|
||||||
sourceRevisionDigest: sha256:ea508e3da23b283f47aeb4384d6b3ab374a27bdffb8d8d8376cedbad3ad77a06
|
sourceRevisionDigest: sha256:db75a06885b897418dcb580679b5f744348573ad317155172346d32442420e5e
|
||||||
sourceBlobDigest: sha256:c210adbd02b0213b9e6f95ffc27f60ec38f8f9040137d55ccf2b650d92ab0ff5
|
sourceBlobDigest: sha256:ac0a3c3a6df002378b3522325c2330ba4309fbbbfa503a04b8267398f20033f5
|
||||||
tags:
|
tags:
|
||||||
- latest
|
- latest
|
||||||
- speakeasy-sdk-regen-1743899436
|
- speakeasy-sdk-regen-1744590653
|
||||||
targets:
|
targets:
|
||||||
plexjava:
|
plexjava:
|
||||||
source: plexapi
|
source: plexapi
|
||||||
sourceNamespace: plexapi
|
sourceNamespace: plexapi
|
||||||
sourceRevisionDigest: sha256:ea508e3da23b283f47aeb4384d6b3ab374a27bdffb8d8d8376cedbad3ad77a06
|
sourceRevisionDigest: sha256:db75a06885b897418dcb580679b5f744348573ad317155172346d32442420e5e
|
||||||
sourceBlobDigest: sha256:c210adbd02b0213b9e6f95ffc27f60ec38f8f9040137d55ccf2b650d92ab0ff5
|
sourceBlobDigest: sha256:ac0a3c3a6df002378b3522325c2330ba4309fbbbfa503a04b8267398f20033f5
|
||||||
codeSamplesNamespace: code-samples-java-plexjava
|
codeSamplesNamespace: code-samples-java-plexjava
|
||||||
codeSamplesRevisionDigest: sha256:c7bc64aee5441f09dae1f6b15a89c94f6b6065f2e25f5528e849e5ddf2f50d8c
|
codeSamplesRevisionDigest: sha256:da1aba30a925f7fffb273f3f1f7d25920debd20e62c974ad4a6904a98697c8db
|
||||||
workflow:
|
workflow:
|
||||||
workflowVersion: 1.0.0
|
workflowVersion: 1.0.0
|
||||||
speakeasyVersion: latest
|
speakeasyVersion: latest
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -69,7 +69,7 @@ The samples below show how a published SDK artifact is used:
|
|||||||
|
|
||||||
Gradle:
|
Gradle:
|
||||||
```groovy
|
```groovy
|
||||||
implementation 'dev.plexapi:plexapi:0.14.2'
|
implementation 'dev.plexapi:plexapi:0.15.0'
|
||||||
```
|
```
|
||||||
|
|
||||||
Maven:
|
Maven:
|
||||||
@@ -77,7 +77,7 @@ Maven:
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>dev.plexapi</groupId>
|
<groupId>dev.plexapi</groupId>
|
||||||
<artifactId>plexapi</artifactId>
|
<artifactId>plexapi</artifactId>
|
||||||
<version>0.14.2</version>
|
<version>0.15.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -94,6 +94,29 @@ On Windows:
|
|||||||
```bash
|
```bash
|
||||||
gradlew.bat publishToMavenLocal -Pskip.signing
|
gradlew.bat publishToMavenLocal -Pskip.signing
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
A logging framework/facade has not yet been adopted but is under consideration.
|
||||||
|
|
||||||
|
For request and response logging (especially json bodies) use:
|
||||||
|
```java
|
||||||
|
SpeakeasyHTTPClient.setDebugLogging(true); // experimental API only (may change without warning)
|
||||||
|
```
|
||||||
|
Example output:
|
||||||
|
```
|
||||||
|
Sending request: http://localhost:35123/bearer#global GET
|
||||||
|
Request headers: {Accept=[application/json], Authorization=[******], Client-Level-Header=[added by client], Idempotency-Key=[some-key], x-speakeasy-user-agent=[speakeasy-sdk/java 0.0.1 internal 0.1.0 org.openapis.openapi]}
|
||||||
|
Received response: (GET http://localhost:35123/bearer#global) 200
|
||||||
|
Response headers: {access-control-allow-credentials=[true], access-control-allow-origin=[*], connection=[keep-alive], content-length=[50], content-type=[application/json], date=[Wed, 09 Apr 2025 01:43:29 GMT], server=[gunicorn/19.9.0]}
|
||||||
|
Response body:
|
||||||
|
{
|
||||||
|
"authenticated": true,
|
||||||
|
"token": "global"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
WARNING: This should only used for temporary debugging purposes. Leaving this option on in a production system could expose credentials/secrets in logs. <i>Authorization</i> headers are redacted by default and there is the ability to specify redacted header names via `SpeakeasyHTTPClient.setRedactedHeaders`.
|
||||||
|
|
||||||
|
Another option is to set the System property `-Djdk.httpclient.HttpClient.log=all`. However, this second option does not log bodies.
|
||||||
<!-- End SDK Installation [installation] -->
|
<!-- End SDK Installation [installation] -->
|
||||||
|
|
||||||
<!-- Start SDK Example Usage [usage] -->
|
<!-- Start SDK Example Usage [usage] -->
|
||||||
|
|||||||
12
RELEASES.md
12
RELEASES.md
@@ -308,4 +308,14 @@ Based on:
|
|||||||
### Generated
|
### Generated
|
||||||
- [java v0.14.2] .
|
- [java v0.14.2] .
|
||||||
### Releases
|
### Releases
|
||||||
- [Maven Central v0.14.2] https://central.sonatype.com/artifact/dev.plexapi/plexapi/0.14.2 - .
|
- [Maven Central v0.14.2] https://central.sonatype.com/artifact/dev.plexapi/plexapi/0.14.2 - .
|
||||||
|
|
||||||
|
## 2025-04-14 00:30:37
|
||||||
|
### Changes
|
||||||
|
Based on:
|
||||||
|
- OpenAPI Doc
|
||||||
|
- Speakeasy CLI 1.531.4 (2.570.4) https://github.com/speakeasy-api/speakeasy
|
||||||
|
### Generated
|
||||||
|
- [java v0.15.0] .
|
||||||
|
### Releases
|
||||||
|
- [Maven Central v0.15.0] https://central.sonatype.com/artifact/dev.plexapi/plexapi/0.15.0 - .
|
||||||
@@ -103,7 +103,7 @@ publishing {
|
|||||||
// https://github.com/gradle/gradle/issues/18619
|
// https://github.com/gradle/gradle/issues/18619
|
||||||
groupId = "dev.plexapi"
|
groupId = "dev.plexapi"
|
||||||
artifactId = "plexapi"
|
artifactId = "plexapi"
|
||||||
version = "0.14.2"
|
version = "0.15.0"
|
||||||
|
|
||||||
from components.java
|
from components.java
|
||||||
|
|
||||||
|
|||||||
@@ -3,26 +3,26 @@
|
|||||||
|
|
||||||
## Fields
|
## Fields
|
||||||
|
|
||||||
| Field | Type | Required | Description | Example |
|
| Field | Type | Required | Description | Example |
|
||||||
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
|
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| `allowSync` | *boolean* | :heavy_check_mark: | Indicates whether syncing is allowed. | false |
|
| `allowSync` | *boolean* | :heavy_check_mark: | Indicates whether syncing is allowed. | false |
|
||||||
| `art` | *String* | :heavy_check_mark: | URL for the background artwork of the media container. | /:/resources/show-fanart.jpg |
|
| `art` | *String* | :heavy_check_mark: | URL for the background artwork of the media container. | /:/resources/show-fanart.jpg |
|
||||||
| `composite` | *String* | :heavy_check_mark: | The relative path to the composite media item. | /library/sections/1/composite/1743824484 |
|
| `composite` | *String* | :heavy_check_mark: | The relative path to the composite media item. | /library/sections/1/composite/1743824484 |
|
||||||
| `filters` | *boolean* | :heavy_check_mark: | UNKNOWN | true |
|
| `filters` | *boolean* | :heavy_check_mark: | UNKNOWN | true |
|
||||||
| `refreshing` | *boolean* | :heavy_check_mark: | Indicates whether the library is currently being refreshed or updated | true |
|
| `refreshing` | *boolean* | :heavy_check_mark: | Indicates whether the library is currently being refreshed or updated | true |
|
||||||
| `thumb` | *String* | :heavy_check_mark: | URL for the thumbnail image of the media container. | /:/resources/show.png |
|
| `thumb` | *String* | :heavy_check_mark: | URL for the thumbnail image of the media container. | /:/resources/show.png |
|
||||||
| `key` | *String* | :heavy_check_mark: | The library key representing the unique identifier | 1 |
|
| `key` | *String* | :heavy_check_mark: | The library key representing the unique identifier | 1 |
|
||||||
| `type` | [GetAllLibrariesType](../../models/operations/GetAllLibrariesType.md) | :heavy_check_mark: | N/A | movie |
|
| `type` | [GetAllLibrariesType](../../models/operations/GetAllLibrariesType.md) | :heavy_check_mark: | N/A | movie |
|
||||||
| `title` | *String* | :heavy_check_mark: | The title of the library | Movies |
|
| `title` | *String* | :heavy_check_mark: | The title of the library | Movies |
|
||||||
| `agent` | *String* | :heavy_check_mark: | The Plex agent used to match and retrieve media metadata. | tv.plex.agents.movie |
|
| `agent` | *String* | :heavy_check_mark: | The Plex agent used to match and retrieve media metadata. | tv.plex.agents.movie |
|
||||||
| `scanner` | *String* | :heavy_check_mark: | UNKNOWN | Plex Movie |
|
| `scanner` | *String* | :heavy_check_mark: | UNKNOWN | Plex Movie |
|
||||||
| `language` | *String* | :heavy_check_mark: | The Plex library language that has been set | en-US |
|
| `language` | *String* | :heavy_check_mark: | The Plex library language that has been set | en-US |
|
||||||
| `uuid` | *String* | :heavy_check_mark: | The universally unique identifier for the library. | e69655a2-ef48-4aba-bb19-01e7d3cc34d6 |
|
| `uuid` | *String* | :heavy_check_mark: | The universally unique identifier for the library. | e69655a2-ef48-4aba-bb19-01e7d3cc34d6 |
|
||||||
| `updatedAt` | *long* | :heavy_check_mark: | Unix epoch datetime in seconds | 1556281940 |
|
| `updatedAt` | *long* | :heavy_check_mark: | Unix epoch datetime in seconds | 1556281940 |
|
||||||
| `createdAt` | *Optional\<Long>* | :heavy_minus_sign: | N/A | 1556281940 |
|
| `createdAt` | *Optional\<Long>* | :heavy_minus_sign: | N/A | 1556281940 |
|
||||||
| `scannedAt` | *long* | :heavy_check_mark: | Unix epoch datetime in seconds | 1556281940 |
|
| `scannedAt` | *long* | :heavy_check_mark: | Unix epoch datetime in seconds | 1556281940 |
|
||||||
| `content` | *boolean* | :heavy_check_mark: | UNKNOWN | true |
|
| `content` | *boolean* | :heavy_check_mark: | UNKNOWN | true |
|
||||||
| `directory` | *boolean* | :heavy_check_mark: | UNKNOWN | true |
|
| `directory` | *boolean* | :heavy_check_mark: | UNKNOWN | true |
|
||||||
| `contentChangedAt` | *int* | :heavy_check_mark: | The number of seconds since the content was last changed relative to now. | 9173960 |
|
| `contentChangedAt` | *long* | :heavy_check_mark: | Timestamp (in seconds) representing the last time the content was modified.<br/>NOTE: Some Plex server have some absurd values for this field, like 8457612157633039800 so it should be int64<br/> | 9173960 |
|
||||||
| `hidden` | [Optional\<Hidden>](../../models/operations/Hidden.md) | :heavy_minus_sign: | N/A | 1 |
|
| `hidden` | [Optional\<Hidden>](../../models/operations/Hidden.md) | :heavy_minus_sign: | N/A | 1 |
|
||||||
| `location` | List\<[GetAllLibrariesLocation](../../models/operations/GetAllLibrariesLocation.md)> | :heavy_check_mark: | N/A | |
|
| `location` | List\<[GetAllLibrariesLocation](../../models/operations/GetAllLibrariesLocation.md)> | :heavy_check_mark: | N/A | |
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
groupId=dev.plexapi
|
groupId=dev.plexapi
|
||||||
artifactId=plexapi
|
artifactId=plexapi
|
||||||
version=0.14.2
|
version=0.15.0
|
||||||
@@ -52,7 +52,7 @@ public class PlexAPI {
|
|||||||
/**
|
/**
|
||||||
* The full address of your Plex Server
|
* The full address of your Plex Server
|
||||||
*/
|
*/
|
||||||
"https://10.10.10.47:32400",
|
"{protocol}://{ip}:{port}",
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ class SDKConfiguration {
|
|||||||
} };
|
} };
|
||||||
private static final String LANGUAGE = "java";
|
private static final String LANGUAGE = "java";
|
||||||
public static final String OPENAPI_DOC_VERSION = "0.0.3";
|
public static final String OPENAPI_DOC_VERSION = "0.0.3";
|
||||||
public static final String SDK_VERSION = "0.14.2";
|
public static final String SDK_VERSION = "0.15.0";
|
||||||
public static final String GEN_VERSION = "2.566.5";
|
public static final String GEN_VERSION = "2.570.4";
|
||||||
private static final String BASE_PACKAGE = "dev.plexapi.sdk";
|
private static final String BASE_PACKAGE = "dev.plexapi.sdk";
|
||||||
public static final String USER_AGENT =
|
public static final String USER_AGENT =
|
||||||
String.format("speakeasy-sdk/%s %s %s %s %s",
|
String.format("speakeasy-sdk/%s %s %s %s %s",
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
|||||||
import dev.plexapi.sdk.utils.LazySingletonValue;
|
import dev.plexapi.sdk.utils.LazySingletonValue;
|
||||||
import dev.plexapi.sdk.utils.Utils;
|
import dev.plexapi.sdk.utils.Utils;
|
||||||
import java.lang.Boolean;
|
import java.lang.Boolean;
|
||||||
import java.lang.Integer;
|
|
||||||
import java.lang.Long;
|
import java.lang.Long;
|
||||||
import java.lang.Override;
|
import java.lang.Override;
|
||||||
import java.lang.String;
|
import java.lang.String;
|
||||||
@@ -127,10 +126,11 @@ public class GetAllLibrariesDirectory {
|
|||||||
private boolean directory;
|
private boolean directory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of seconds since the content was last changed relative to now.
|
* Timestamp (in seconds) representing the last time the content was modified.
|
||||||
|
* NOTE: Some Plex server have some absurd values for this field, like 8457612157633039800 so it should be int64
|
||||||
*/
|
*/
|
||||||
@JsonProperty("contentChangedAt")
|
@JsonProperty("contentChangedAt")
|
||||||
private int contentChangedAt;
|
private long contentChangedAt;
|
||||||
|
|
||||||
@JsonInclude(Include.NON_ABSENT)
|
@JsonInclude(Include.NON_ABSENT)
|
||||||
@JsonProperty("hidden")
|
@JsonProperty("hidden")
|
||||||
@@ -159,7 +159,7 @@ public class GetAllLibrariesDirectory {
|
|||||||
@JsonProperty("scannedAt") long scannedAt,
|
@JsonProperty("scannedAt") long scannedAt,
|
||||||
@JsonProperty("content") boolean content,
|
@JsonProperty("content") boolean content,
|
||||||
@JsonProperty("directory") boolean directory,
|
@JsonProperty("directory") boolean directory,
|
||||||
@JsonProperty("contentChangedAt") int contentChangedAt,
|
@JsonProperty("contentChangedAt") long contentChangedAt,
|
||||||
@JsonProperty("hidden") Optional<? extends Hidden> hidden,
|
@JsonProperty("hidden") Optional<? extends Hidden> hidden,
|
||||||
@JsonProperty("Location") List<GetAllLibrariesLocation> location) {
|
@JsonProperty("Location") List<GetAllLibrariesLocation> location) {
|
||||||
Utils.checkNotNull(allowSync, "allowSync");
|
Utils.checkNotNull(allowSync, "allowSync");
|
||||||
@@ -224,7 +224,7 @@ public class GetAllLibrariesDirectory {
|
|||||||
long scannedAt,
|
long scannedAt,
|
||||||
boolean content,
|
boolean content,
|
||||||
boolean directory,
|
boolean directory,
|
||||||
int contentChangedAt,
|
long contentChangedAt,
|
||||||
List<GetAllLibrariesLocation> location) {
|
List<GetAllLibrariesLocation> location) {
|
||||||
this(allowSync, art, composite, filters, refreshing, thumb, key, type, title, agent, scanner, language, uuid, updatedAt, Optional.empty(), scannedAt, content, directory, contentChangedAt, Optional.empty(), location);
|
this(allowSync, art, composite, filters, refreshing, thumb, key, type, title, agent, scanner, language, uuid, updatedAt, Optional.empty(), scannedAt, content, directory, contentChangedAt, Optional.empty(), location);
|
||||||
}
|
}
|
||||||
@@ -368,10 +368,11 @@ public class GetAllLibrariesDirectory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of seconds since the content was last changed relative to now.
|
* Timestamp (in seconds) representing the last time the content was modified.
|
||||||
|
* NOTE: Some Plex server have some absurd values for this field, like 8457612157633039800 so it should be int64
|
||||||
*/
|
*/
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public int contentChangedAt() {
|
public long contentChangedAt() {
|
||||||
return contentChangedAt;
|
return contentChangedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,9 +554,10 @@ public class GetAllLibrariesDirectory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of seconds since the content was last changed relative to now.
|
* Timestamp (in seconds) representing the last time the content was modified.
|
||||||
|
* NOTE: Some Plex server have some absurd values for this field, like 8457612157633039800 so it should be int64
|
||||||
*/
|
*/
|
||||||
public GetAllLibrariesDirectory withContentChangedAt(int contentChangedAt) {
|
public GetAllLibrariesDirectory withContentChangedAt(long contentChangedAt) {
|
||||||
Utils.checkNotNull(contentChangedAt, "contentChangedAt");
|
Utils.checkNotNull(contentChangedAt, "contentChangedAt");
|
||||||
this.contentChangedAt = contentChangedAt;
|
this.contentChangedAt = contentChangedAt;
|
||||||
return this;
|
return this;
|
||||||
@@ -703,7 +705,7 @@ public class GetAllLibrariesDirectory {
|
|||||||
|
|
||||||
private Boolean directory;
|
private Boolean directory;
|
||||||
|
|
||||||
private Integer contentChangedAt;
|
private Long contentChangedAt;
|
||||||
|
|
||||||
private Optional<? extends Hidden> hidden;
|
private Optional<? extends Hidden> hidden;
|
||||||
|
|
||||||
@@ -876,9 +878,10 @@ public class GetAllLibrariesDirectory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of seconds since the content was last changed relative to now.
|
* Timestamp (in seconds) representing the last time the content was modified.
|
||||||
|
* NOTE: Some Plex server have some absurd values for this field, like 8457612157633039800 so it should be int64
|
||||||
*/
|
*/
|
||||||
public Builder contentChangedAt(int contentChangedAt) {
|
public Builder contentChangedAt(long contentChangedAt) {
|
||||||
Utils.checkNotNull(contentChangedAt, "contentChangedAt");
|
Utils.checkNotNull(contentChangedAt, "contentChangedAt");
|
||||||
this.contentChangedAt = contentChangedAt;
|
this.contentChangedAt = contentChangedAt;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -4,18 +4,139 @@
|
|||||||
package dev.plexapi.sdk.utils;
|
package dev.plexapi.sdk.utils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpHeaders;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpRequest.BodyPublishers;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.io.InputStream;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class SpeakeasyHTTPClient implements HTTPClient {
|
public class SpeakeasyHTTPClient implements HTTPClient {
|
||||||
|
|
||||||
|
private static boolean debugEnabled = false;
|
||||||
|
|
||||||
|
// uppercase
|
||||||
|
private static Set<String> redactedHeaders = Set.of("AUTHORIZATION", "X-API-KEY");
|
||||||
|
|
||||||
|
private static Consumer<? super String> logger = System.out::println;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Experimental, may be changed anytime. Sets debug logging on or off for
|
||||||
|
* requests and responses including bodies for JSON content. WARNING: this
|
||||||
|
* setting may expose sensitive information in logs (like <i>Authorization</i>
|
||||||
|
* headers), and should only be enabled temporarily for local debugging
|
||||||
|
* purposes. By default, <i>Authorization</i> headers are redacted in the logs
|
||||||
|
* ( printed with a value of {@code [*******]}). Header suppression is controlled
|
||||||
|
* with the {@link #setRedactedHeaders(Collection)} method.
|
||||||
|
*
|
||||||
|
* @param enabled true to enable debug logging, false to disable it
|
||||||
|
*/
|
||||||
|
public static void setDebugLogging(boolean enabled) {
|
||||||
|
debugEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Experimental, may be changed anytime. When debug logging is enabled this
|
||||||
|
* method controls the suppression of header values in the logs. By default,
|
||||||
|
* <i>Authorization</i> headers are redacted in the logs (printed with a value
|
||||||
|
* of {@code [*******]}). Header suppression is controlled with the
|
||||||
|
* {@link #setRedactedHeaders(Collection)} method.
|
||||||
|
*
|
||||||
|
* @param headerNames the names (case-insensitive) of the headers whose values
|
||||||
|
* will be redacted in the logs
|
||||||
|
*/
|
||||||
|
public static void setRedactedHeaders(Collection<String> headerNames) {
|
||||||
|
redactedHeaders = headerNames.stream() //
|
||||||
|
.map(x -> x.toUpperCase(Locale.ENGLISH)) //
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setLogger(Consumer<? super String> logger) {
|
||||||
|
SpeakeasyHTTPClient.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpResponse<InputStream> send(HttpRequest request)
|
public HttpResponse<InputStream> send(HttpRequest request)
|
||||||
throws IOException, InterruptedException, URISyntaxException {
|
throws IOException, InterruptedException, URISyntaxException {
|
||||||
HttpClient client = HttpClient.newHttpClient();
|
HttpClient client = HttpClient.newHttpClient();
|
||||||
return client.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
if (debugEnabled) {
|
||||||
|
request = logRequest(request);
|
||||||
|
}
|
||||||
|
var response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||||
|
if (debugEnabled) {
|
||||||
|
response = logResponse(response);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpRequest logRequest(HttpRequest request) {
|
||||||
|
log("Sending request: " + request);
|
||||||
|
log("Request headers: " + redactHeaders(request.headers()));
|
||||||
|
// only log the body if it is present and the content type is JSON
|
||||||
|
if (request.bodyPublisher().isPresent() && request.headers() //
|
||||||
|
.firstValue("Content-Type") //
|
||||||
|
.filter(x -> x.equals("application/json") || x.equals("text/plain")).isPresent()) {
|
||||||
|
// we read the body and ensure that the BodyPublisher is rebuilt to pass to the
|
||||||
|
// http client
|
||||||
|
byte[] body = Helpers.bodyBytes(request);
|
||||||
|
request = Helpers //
|
||||||
|
.copy(request) //
|
||||||
|
.method(request.method(), BodyPublishers.ofByteArray(body)) //
|
||||||
|
.build();
|
||||||
|
// note that in the case of text/plain a different encoding from UTF-8
|
||||||
|
// may be in use but we just log the bytes as UTF-8. Unexpected encodings
|
||||||
|
// do not throw (substitution happens).
|
||||||
|
log("Request body:\n" + new String(body, StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpResponse<InputStream> logResponse(HttpResponse<InputStream> response) throws IOException {
|
||||||
|
// make the response re-readable by loading the response body into a byte array
|
||||||
|
// and allowing the InputStream to be read many times
|
||||||
|
response = Utils.cache(response);
|
||||||
|
log("Received response: " + response);
|
||||||
|
log("Response headers: " + redactHeaders(response.headers()));
|
||||||
|
// only log the response body if it is present and the content type is JSON or plain text
|
||||||
|
if (response.headers() //
|
||||||
|
.firstValue("Content-Type") //
|
||||||
|
.filter(x -> x.equals("application/json") || x.equals("text/plain")) //
|
||||||
|
.isPresent()) {
|
||||||
|
// the response is re-readable so we can read and close it without
|
||||||
|
// affecting later processing of the response.
|
||||||
|
|
||||||
|
// note that in the case of text/plain a different encoding from UTF-8
|
||||||
|
// may be in use but we just log the bytes as UTF-8. Unexpected encodings
|
||||||
|
// do not throw (substitution happens).
|
||||||
|
log("Response body:\n" + Utils.toUtf8AndClose(response.body()));
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String redactHeaders(HttpHeaders headers) {
|
||||||
|
return "{" + headers.map() //
|
||||||
|
.entrySet() //
|
||||||
|
.stream() //
|
||||||
|
.map(entry -> {
|
||||||
|
final String value;
|
||||||
|
if (redactedHeaders.contains(entry.getKey().toUpperCase(Locale.ENGLISH))) {
|
||||||
|
value = "[******]";
|
||||||
|
} else {
|
||||||
|
value = String.valueOf(entry.getValue());
|
||||||
|
}
|
||||||
|
return entry.getKey() + "=" + value;
|
||||||
|
}) //
|
||||||
|
.collect(Collectors.joining(", ")) + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void log(String message) {
|
||||||
|
logger.accept(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1368,20 +1368,32 @@ public final class Utils {
|
|||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T valueOrNull(T value) {
|
public static <T> T valueOrElse(T value, T valueIfNotPresent) {
|
||||||
return value;
|
return value != null ? value : valueIfNotPresent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T valueOrElse(Optional<T> value, T valueIfNotPresent) {
|
||||||
|
return value.orElse(valueIfNotPresent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T valueOrNull(Optional<T> value) {
|
public static <T> T valueOrElse(JsonNullable<T> value, T valueIfNotPresent) {
|
||||||
return value.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> T valueOrNull(JsonNullable<T> value) {
|
|
||||||
if (value.isPresent()) {
|
if (value.isPresent()) {
|
||||||
return value.get();
|
return value.get();
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return valueIfNotPresent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> T valueOrNull(T value) {
|
||||||
|
return valueOrElse(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T valueOrNull(Optional<T> value) {
|
||||||
|
return valueOrElse(value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T valueOrNull(JsonNullable<T> value) {
|
||||||
|
return valueOrElse(value, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user