mirror of https://github.com/halo-dev/halo
feat: API to save external links as attachments (#6364)
#### What type of PR is this? /kind api-change /kind feature /area core #### What this PR does / why we need it: see #2335 增加将第三方资源转存为附件资源的接口。 `/apis/api.console.halo.run/v1alpha1/attachments/-/upload-from-url` UC: `/apis/uc.api.content.halo.run/v1alpha1/attachments/-/upload-from-url` 其中参数为 ```json { "url": "string", "filename": "string", "groupName": "string", "policyName": "string" } ``` #### How to test it? 测试能否将第三方接口的资源保存至附件中。 测试各类附件,例如图片、视频、文本等。 #### Does this PR introduce a user-facing change? ```release-note 增加通过链接转存第三方资源至附件库的接口 ```pull/6515/head
parent
21db06a507
commit
e5bbbb3b7b
|
@ -2250,6 +2250,36 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/apis/api.console.halo.run/v1alpha1/attachments/-/upload-from-url": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "ExternalTransferAttachment",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/UploadFromUrlRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"default": {
|
||||||
|
"content": {
|
||||||
|
"*/*": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Attachment"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "default response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"AttachmentV1alpha1Console"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/apis/api.console.halo.run/v1alpha1/attachments/upload": {
|
"/apis/api.console.halo.run/v1alpha1/attachments/upload": {
|
||||||
"post": {
|
"post": {
|
||||||
"operationId": "UploadAttachment",
|
"operationId": "UploadAttachment",
|
||||||
|
@ -14136,6 +14166,47 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/apis/uc.api.content.halo.run/v1alpha1/attachments/-/upload-from-url": {
|
||||||
|
"post": {
|
||||||
|
"description": "Upload attachment from the given URL.",
|
||||||
|
"operationId": "ExternalTransferAttachment_1",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Wait for permalink.",
|
||||||
|
"in": "query",
|
||||||
|
"name": "waitForPermalink",
|
||||||
|
"schema": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/UploadFromUrlRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"default": {
|
||||||
|
"content": {
|
||||||
|
"*/*": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Attachment"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "default response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"AttachmentV1alpha1Uc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/apis/uc.api.content.halo.run/v1alpha1/posts": {
|
"/apis/uc.api.content.halo.run/v1alpha1/posts": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "List posts owned by the current user.",
|
"description": "List posts owned by the current user.",
|
||||||
|
@ -22621,6 +22692,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"UploadFromUrlRequest": {
|
||||||
|
"required": [
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"filename": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"User": {
|
"User": {
|
||||||
"required": [
|
"required": [
|
||||||
"apiVersion",
|
"apiVersion",
|
||||||
|
|
|
@ -117,6 +117,36 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/apis/api.console.halo.run/v1alpha1/attachments/-/upload-from-url": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "ExternalTransferAttachment",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/UploadFromUrlRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"default": {
|
||||||
|
"content": {
|
||||||
|
"*/*": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Attachment"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "default response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"AttachmentV1alpha1Console"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/apis/api.console.halo.run/v1alpha1/attachments/upload": {
|
"/apis/api.console.halo.run/v1alpha1/attachments/upload": {
|
||||||
"post": {
|
"post": {
|
||||||
"operationId": "UploadAttachment",
|
"operationId": "UploadAttachment",
|
||||||
|
@ -6246,6 +6276,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"UploadFromUrlRequest": {
|
||||||
|
"required": [
|
||||||
|
"policyName",
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"filename": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"groupName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"policyName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"User": {
|
"User": {
|
||||||
"required": [
|
"required": [
|
||||||
"apiVersion",
|
"apiVersion",
|
||||||
|
|
|
@ -57,6 +57,47 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/apis/uc.api.content.halo.run/v1alpha1/attachments/-/upload-from-url": {
|
||||||
|
"post": {
|
||||||
|
"description": "Upload attachment from the given URL.",
|
||||||
|
"operationId": "ExternalTransferAttachment",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Wait for permalink.",
|
||||||
|
"in": "query",
|
||||||
|
"name": "waitForPermalink",
|
||||||
|
"schema": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/UploadFromUrlRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"default": {
|
||||||
|
"content": {
|
||||||
|
"*/*": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Attachment"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "default response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"AttachmentV1alpha1Uc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/apis/uc.api.content.halo.run/v1alpha1/posts": {
|
"/apis/uc.api.content.halo.run/v1alpha1/posts": {
|
||||||
"get": {
|
"get": {
|
||||||
"description": "List posts owned by the current user.",
|
"description": "List posts owned by the current user.",
|
||||||
|
@ -1943,6 +1984,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"UploadFromUrlRequest": {
|
||||||
|
"required": [
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"filename": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"UserDevice": {
|
"UserDevice": {
|
||||||
"required": [
|
"required": [
|
||||||
"active",
|
"active",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package run.halo.app.core.extension.service;
|
package run.halo.app.core.extension.service;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
@ -91,4 +92,15 @@ public interface AttachmentService {
|
||||||
*/
|
*/
|
||||||
Mono<URI> getSharedURL(Attachment attachment, Duration ttl);
|
Mono<URI> getSharedURL(Attachment attachment, Duration ttl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfer external links to attachments.
|
||||||
|
*
|
||||||
|
* @param url external url
|
||||||
|
* @param policyName policy name
|
||||||
|
* @param groupName group name
|
||||||
|
* @param filename filename
|
||||||
|
* @return attachment
|
||||||
|
*/
|
||||||
|
Mono<Attachment> uploadFromUrl(@NonNull URL url, @NonNull String policyName,
|
||||||
|
String groupName, String filename);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,10 @@ import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldS
|
||||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
@ -102,6 +104,29 @@ public class AttachmentEndpoint implements CustomEndpoint {
|
||||||
))
|
))
|
||||||
.response(responseBuilder().implementation(Attachment.class))
|
.response(responseBuilder().implementation(Attachment.class))
|
||||||
.build())
|
.build())
|
||||||
|
.POST("/attachments/-/upload-from-url", contentType(MediaType.APPLICATION_JSON),
|
||||||
|
request -> request.bodyToMono(UploadFromUrlRequest.class)
|
||||||
|
.flatMap(uploadFromUrlRequest -> {
|
||||||
|
var url = uploadFromUrlRequest.url();
|
||||||
|
var policyName = uploadFromUrlRequest.policyName();
|
||||||
|
var groupName = uploadFromUrlRequest.groupName();
|
||||||
|
var fileName = uploadFromUrlRequest.filename();
|
||||||
|
return attachmentService.uploadFromUrl(url, policyName,
|
||||||
|
groupName, fileName);
|
||||||
|
})
|
||||||
|
.flatMap(attachment -> ServerResponse.ok().bodyValue(attachment)),
|
||||||
|
builder -> builder
|
||||||
|
.operationId("ExternalTransferAttachment")
|
||||||
|
.tag(tag)
|
||||||
|
.requestBody(requestBodyBuilder()
|
||||||
|
.required(true)
|
||||||
|
.content(contentBuilder()
|
||||||
|
.mediaType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
.schema(schemaBuilder().implementation(UploadFromUrlRequest.class))
|
||||||
|
))
|
||||||
|
.response(responseBuilder().implementation(Attachment.class))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.GET("/attachments", this::search,
|
.GET("/attachments", this::search,
|
||||||
builder -> {
|
builder -> {
|
||||||
builder
|
builder
|
||||||
|
@ -275,6 +300,21 @@ public class AttachmentEndpoint implements CustomEndpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record UploadFromUrlRequest(@Schema(requiredMode = REQUIRED) URL url,
|
||||||
|
@Schema(requiredMode = REQUIRED) String policyName,
|
||||||
|
String groupName,
|
||||||
|
String filename) {
|
||||||
|
public UploadFromUrlRequest {
|
||||||
|
if (Objects.isNull(url)) {
|
||||||
|
throw new ServerWebInputException("Required url is missing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.hasText(policyName)) {
|
||||||
|
throw new ServerWebInputException("Policy name must not be blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Schema(types = "object")
|
@Schema(types = "object")
|
||||||
public interface IUploadRequest {
|
public interface IUploadRequest {
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package run.halo.app.core.extension.service.impl;
|
package run.halo.app.core.extension.service.impl;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.codec.multipart.FilePart;
|
import org.springframework.http.codec.multipart.FilePart;
|
||||||
|
@ -29,6 +33,7 @@ import run.halo.app.core.extension.attachment.endpoint.UploadOption;
|
||||||
import run.halo.app.core.extension.service.AttachmentService;
|
import run.halo.app.core.extension.service.AttachmentService;
|
||||||
import run.halo.app.extension.ConfigMap;
|
import run.halo.app.extension.ConfigMap;
|
||||||
import run.halo.app.extension.ReactiveExtensionClient;
|
import run.halo.app.extension.ReactiveExtensionClient;
|
||||||
|
import run.halo.app.infra.ReactiveUrlDataBufferFetcher;
|
||||||
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
|
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
@ -38,10 +43,14 @@ public class DefaultAttachmentService implements AttachmentService {
|
||||||
|
|
||||||
private final ExtensionGetter extensionGetter;
|
private final ExtensionGetter extensionGetter;
|
||||||
|
|
||||||
|
private final ReactiveUrlDataBufferFetcher dataBufferFetcher;
|
||||||
|
|
||||||
public DefaultAttachmentService(ReactiveExtensionClient client,
|
public DefaultAttachmentService(ReactiveExtensionClient client,
|
||||||
ExtensionGetter extensionGetter) {
|
ExtensionGetter extensionGetter,
|
||||||
|
ReactiveUrlDataBufferFetcher dataBufferFetcher) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.extensionGetter = extensionGetter;
|
this.extensionGetter = extensionGetter;
|
||||||
|
this.dataBufferFetcher = dataBufferFetcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -130,6 +139,45 @@ public class DefaultAttachmentService implements AttachmentService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Attachment> uploadFromUrl(@NonNull URL url, @NonNull String policyName,
|
||||||
|
String groupName, String filename) {
|
||||||
|
var uri = URI.create(url.toString());
|
||||||
|
AtomicReference<MediaType> mediaTypeRef = new AtomicReference<>();
|
||||||
|
AtomicReference<String> fileNameRef = new AtomicReference<>(filename);
|
||||||
|
|
||||||
|
Mono<Flux<DataBuffer>> contentMono = dataBufferFetcher.head(uri)
|
||||||
|
.map(response -> {
|
||||||
|
var httpHeaders = response.getHeaders();
|
||||||
|
if (!StringUtils.hasText(fileNameRef.get())) {
|
||||||
|
fileNameRef.set(getExternalUrlFilename(uri, httpHeaders));
|
||||||
|
}
|
||||||
|
MediaType contentType = httpHeaders.getContentType();
|
||||||
|
mediaTypeRef.set(contentType);
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.map(response -> dataBufferFetcher.fetch(uri));
|
||||||
|
|
||||||
|
return contentMono.flatMap(
|
||||||
|
(content) -> upload(policyName, groupName, fileNameRef.get(), content,
|
||||||
|
mediaTypeRef.get())
|
||||||
|
)
|
||||||
|
.onErrorResume(throwable -> Mono.error(
|
||||||
|
new ServerWebInputException(
|
||||||
|
"Failed to transfer the attachment from the external URL."))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getExternalUrlFilename(URI externalUrl, HttpHeaders httpHeaders) {
|
||||||
|
String fileName = httpHeaders.getContentDisposition().getFilename();
|
||||||
|
if (!StringUtils.hasText(fileName)) {
|
||||||
|
var path = externalUrl.getPath();
|
||||||
|
fileName = Paths.get(path).getFileName().toString();
|
||||||
|
}
|
||||||
|
// TODO get file extension from media type
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
private <T> Mono<T> authenticationConsumer(Function<Authentication, Mono<T>> func) {
|
private <T> Mono<T> authenticationConsumer(Function<Authentication, Mono<T>> func) {
|
||||||
return ReactiveSecurityContextHolder.getContext()
|
return ReactiveSecurityContextHolder.getContext()
|
||||||
.switchIfEmpty(Mono.error(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED,
|
.switchIfEmpty(Mono.error(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED,
|
||||||
|
|
|
@ -8,10 +8,13 @@ import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder;
|
||||||
import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder;
|
import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder;
|
||||||
import static org.springdoc.core.fn.builders.schema.Builder.schemaBuilder;
|
import static org.springdoc.core.fn.builders.schema.Builder.schemaBuilder;
|
||||||
import static org.springdoc.webflux.core.fn.SpringdocRouteBuilder.route;
|
import static org.springdoc.webflux.core.fn.SpringdocRouteBuilder.route;
|
||||||
|
import static org.springframework.web.reactive.function.server.RequestPredicates.contentType;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
@ -79,27 +82,63 @@ public class UcPostAttachmentEndpoint implements CustomEndpoint {
|
||||||
)
|
)
|
||||||
.response(responseBuilder().implementation(Attachment.class))
|
.response(responseBuilder().implementation(Attachment.class))
|
||||||
)
|
)
|
||||||
|
.POST("/attachments/-/upload-from-url", contentType(MediaType.APPLICATION_JSON),
|
||||||
|
this::uploadFromUrlForPost,
|
||||||
|
builder -> builder
|
||||||
|
.operationId("ExternalTransferAttachment")
|
||||||
|
.description("Upload attachment from the given URL.")
|
||||||
|
.tag(tag)
|
||||||
|
.parameter(parameterBuilder()
|
||||||
|
.name("waitForPermalink")
|
||||||
|
.description("Wait for permalink.")
|
||||||
|
.in(ParameterIn.QUERY)
|
||||||
|
.required(false)
|
||||||
|
.implementation(boolean.class))
|
||||||
|
.requestBody(requestBodyBuilder()
|
||||||
|
.required(true)
|
||||||
|
.content(contentBuilder()
|
||||||
|
.mediaType(MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
.schema(schemaBuilder().implementation(UploadFromUrlRequest.class))
|
||||||
|
))
|
||||||
|
.response(responseBuilder().implementation(Attachment.class))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Mono<ServerResponse> uploadFromUrlForPost(ServerRequest request) {
|
||||||
|
var uploadFromUrlRequestMono = request.bodyToMono(UploadFromUrlRequest.class);
|
||||||
|
|
||||||
|
var uploadAttachment = getPostSettingMono()
|
||||||
|
.flatMap(postSetting -> uploadFromUrlRequestMono.flatMap(
|
||||||
|
uploadFromUrlRequest -> {
|
||||||
|
var url = uploadFromUrlRequest.url();
|
||||||
|
var fileName = uploadFromUrlRequest.filename();
|
||||||
|
return attachmentService.uploadFromUrl(url,
|
||||||
|
postSetting.getAttachmentPolicyName(),
|
||||||
|
postSetting.getAttachmentGroupName(),
|
||||||
|
fileName
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
var waitForPermalink = request.queryParam("waitForPermalink")
|
||||||
|
.map(Boolean::valueOf)
|
||||||
|
.orElse(false);
|
||||||
|
if (waitForPermalink) {
|
||||||
|
uploadAttachment = waitForPermalink(uploadAttachment);
|
||||||
|
}
|
||||||
|
return ServerResponse.ok().body(uploadAttachment, Attachment.class);
|
||||||
|
}
|
||||||
|
|
||||||
private Mono<ServerResponse> createAttachmentForPost(ServerRequest request) {
|
private Mono<ServerResponse> createAttachmentForPost(ServerRequest request) {
|
||||||
var postAttachmentRequestMono = request.body(BodyExtractors.toMultipartData())
|
var postAttachmentRequestMono = request.body(BodyExtractors.toMultipartData())
|
||||||
.map(PostAttachmentRequest::from)
|
.map(PostAttachmentRequest::from)
|
||||||
.cache();
|
.cache();
|
||||||
|
|
||||||
var postSettingMono = systemSettingFetcher.fetchPost()
|
|
||||||
.<SystemSetting.Post>handle((postSetting, sink) -> {
|
|
||||||
var attachmentPolicyName = postSetting.getAttachmentPolicyName();
|
|
||||||
if (StringUtils.isBlank(attachmentPolicyName)) {
|
|
||||||
sink.error(new ServerWebInputException(
|
|
||||||
"Please configure storage policy for post attachment first."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sink.next(postSetting);
|
|
||||||
});
|
|
||||||
|
|
||||||
// get settings
|
// get settings
|
||||||
var createdAttachment = postSettingMono.flatMap(postSetting -> postAttachmentRequestMono
|
var createdAttachment =
|
||||||
|
getPostSettingMono().flatMap(postSetting -> postAttachmentRequestMono
|
||||||
.flatMap(postAttachmentRequest -> getCurrentUser().flatMap(
|
.flatMap(postAttachmentRequest -> getCurrentUser().flatMap(
|
||||||
username -> attachmentService.upload(username,
|
username -> attachmentService.upload(username,
|
||||||
postSetting.getAttachmentPolicyName(),
|
postSetting.getAttachmentPolicyName(),
|
||||||
|
@ -111,6 +150,12 @@ public class UcPostAttachmentEndpoint implements CustomEndpoint {
|
||||||
.map(Boolean::valueOf)
|
.map(Boolean::valueOf)
|
||||||
.orElse(false);
|
.orElse(false);
|
||||||
if (waitForPermalink) {
|
if (waitForPermalink) {
|
||||||
|
createdAttachment = waitForPermalink(createdAttachment);
|
||||||
|
}
|
||||||
|
return ServerResponse.ok().body(createdAttachment, Attachment.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mono<Attachment> waitForPermalink(Mono<Attachment> createdAttachment) {
|
||||||
createdAttachment = createdAttachment.flatMap(attachment ->
|
createdAttachment = createdAttachment.flatMap(attachment ->
|
||||||
attachmentService.getPermalink(attachment)
|
attachmentService.getPermalink(attachment)
|
||||||
.doOnNext(permalink -> {
|
.doOnNext(permalink -> {
|
||||||
|
@ -122,8 +167,19 @@ public class UcPostAttachmentEndpoint implements CustomEndpoint {
|
||||||
status.setPermalink(permalink.toString());
|
status.setPermalink(permalink.toString());
|
||||||
})
|
})
|
||||||
.thenReturn(attachment));
|
.thenReturn(attachment));
|
||||||
|
return createdAttachment;
|
||||||
}
|
}
|
||||||
return ServerResponse.ok().body(createdAttachment, Attachment.class);
|
|
||||||
|
private Mono<SystemSetting.Post> getPostSettingMono() {
|
||||||
|
return systemSettingFetcher.fetchPost().handle((postSetting, sink) -> {
|
||||||
|
var attachmentPolicyName = postSetting.getAttachmentPolicyName();
|
||||||
|
if (StringUtils.isBlank(attachmentPolicyName)) {
|
||||||
|
sink.error(new ServerWebInputException(
|
||||||
|
"Please configure storage policy for post attachment first."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sink.next(postSetting);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Consumer<Attachment> linkWith(PostAttachmentRequest request) {
|
private Consumer<Attachment> linkWith(PostAttachmentRequest request) {
|
||||||
|
@ -165,6 +221,15 @@ public class UcPostAttachmentEndpoint implements CustomEndpoint {
|
||||||
return GroupVersion.parseAPIVersion("uc.api.content.halo.run/v1alpha1");
|
return GroupVersion.parseAPIVersion("uc.api.content.halo.run/v1alpha1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record UploadFromUrlRequest(@Schema(requiredMode = REQUIRED) URL url,
|
||||||
|
String filename) {
|
||||||
|
public UploadFromUrlRequest {
|
||||||
|
if (Objects.isNull(url)) {
|
||||||
|
throw new ServerWebInputException("Required url is missing.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Schema(types = "object")
|
@Schema(types = "object")
|
||||||
public record PostAttachmentRequest(
|
public record PostAttachmentRequest(
|
||||||
@Schema(requiredMode = REQUIRED, description = "Attachment data.")
|
@Schema(requiredMode = REQUIRED, description = "Attachment data.")
|
||||||
|
|
|
@ -3,10 +3,12 @@ package run.halo.app.infra;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.reactive.function.client.WebClient;
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.netty.http.client.HttpClient;
|
import reactor.netty.http.client.HttpClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,4 +33,12 @@ public class DefaultReactiveUrlDataBufferFetcher implements ReactiveUrlDataBuffe
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.bodyToFlux(DataBuffer.class);
|
.bodyToFlux(DataBuffer.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<ResponseEntity<Void>> head(URI uri) {
|
||||||
|
return webClient.head()
|
||||||
|
.uri(uri)
|
||||||
|
.retrieve()
|
||||||
|
.toBodilessEntity();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@ package run.halo.app.infra;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import org.springframework.core.io.buffer.DataBuffer;
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>{@link DataBuffer} stream fetcher from uri.</p>
|
* <p>{@link DataBuffer} stream fetcher from uri.</p>
|
||||||
|
@ -10,7 +12,6 @@ import reactor.core.publisher.Flux;
|
||||||
* @author guqing
|
* @author guqing
|
||||||
* @since 2.6.0
|
* @since 2.6.0
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
|
||||||
public interface ReactiveUrlDataBufferFetcher {
|
public interface ReactiveUrlDataBufferFetcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,4 +21,12 @@ public interface ReactiveUrlDataBufferFetcher {
|
||||||
* @return data buffer flux
|
* @return data buffer flux
|
||||||
*/
|
*/
|
||||||
Flux<DataBuffer> fetch(URI uri);
|
Flux<DataBuffer> fetch(URI uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Get head of the uri.</p>
|
||||||
|
*
|
||||||
|
* @param uri uri to fetch
|
||||||
|
* @return response entity
|
||||||
|
*/
|
||||||
|
Mono<ResponseEntity<Void>> head(URI uri);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package run.halo.app.infra.utils;
|
package run.halo.app.infra.utils;
|
||||||
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
@ -9,6 +10,20 @@ public final class FileNameUtils {
|
||||||
private FileNameUtils() {
|
private FileNameUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the file name has an extension.
|
||||||
|
*
|
||||||
|
* @param filename is name of file.
|
||||||
|
* @return True if file name has extension, otherwise false.
|
||||||
|
*/
|
||||||
|
public static boolean hasFileExtension(String filename) {
|
||||||
|
if (filename == null || filename.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var extensionRegex = ".*\\.[a-zA-Z0-9]+$";
|
||||||
|
return Pattern.matches(extensionRegex, filename);
|
||||||
|
}
|
||||||
|
|
||||||
public static String removeFileExtension(String filename, boolean removeAllExtensions) {
|
public static String removeFileExtension(String filename, boolean removeAllExtensions) {
|
||||||
if (filename == null || filename.isEmpty()) {
|
if (filename == null || filename.isEmpty()) {
|
||||||
return filename;
|
return filename;
|
||||||
|
|
|
@ -17,10 +17,15 @@ rules:
|
||||||
- apiGroups: [ "api.console.halo.run" ]
|
- apiGroups: [ "api.console.halo.run" ]
|
||||||
resources: [ "attachments" ]
|
resources: [ "attachments" ]
|
||||||
verbs: [ "*" ]
|
verbs: [ "*" ]
|
||||||
|
- apiGroups: [ "api.console.halo.run" ]
|
||||||
|
resources: [ "attachments/upload-from-url" ]
|
||||||
|
verbs: [ "create" ]
|
||||||
- apiGroups: [ "" ]
|
- apiGroups: [ "" ]
|
||||||
resources: [ "settings" ]
|
resources: [ "settings" ]
|
||||||
verbs: [ "get" ]
|
verbs: [ "get" ]
|
||||||
- nonResourceURLs: [ "/apis/api.console.halo.run/v1alpha1/attachments/upload" ]
|
- nonResourceURLs: [
|
||||||
|
"/apis/api.console.halo.run/v1alpha1/attachments/upload"
|
||||||
|
]
|
||||||
verbs: [ "create" ]
|
verbs: [ "create" ]
|
||||||
---
|
---
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
|
|
|
@ -114,3 +114,6 @@ rules:
|
||||||
- apiGroups: [ "uc.api.content.halo.run" ]
|
- apiGroups: [ "uc.api.content.halo.run" ]
|
||||||
resources: [ "attachments" ]
|
resources: [ "attachments" ]
|
||||||
verbs: [ "create", "update", "delete" ]
|
verbs: [ "create", "update", "delete" ]
|
||||||
|
- apiGroups: [ "uc.api.content.halo.run" ]
|
||||||
|
resources: [ "attachments/upload-from-url" ]
|
||||||
|
verbs: [ "create" ]
|
||||||
|
|
|
@ -18,8 +18,11 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.http.HttpStatusCode;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.http.client.MultipartBodyBuilder;
|
import org.springframework.http.client.MultipartBodyBuilder;
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
import org.springframework.web.reactive.function.BodyInserters;
|
import org.springframework.web.reactive.function.BodyInserters;
|
||||||
|
@ -35,6 +38,7 @@ import run.halo.app.extension.ListResult;
|
||||||
import run.halo.app.extension.Metadata;
|
import run.halo.app.extension.Metadata;
|
||||||
import run.halo.app.extension.PageRequest;
|
import run.halo.app.extension.PageRequest;
|
||||||
import run.halo.app.extension.ReactiveExtensionClient;
|
import run.halo.app.extension.ReactiveExtensionClient;
|
||||||
|
import run.halo.app.infra.ReactiveUrlDataBufferFetcher;
|
||||||
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
|
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
@ -46,13 +50,17 @@ class AttachmentEndpointTest {
|
||||||
@Mock
|
@Mock
|
||||||
ExtensionGetter extensionGetter;
|
ExtensionGetter extensionGetter;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
ReactiveUrlDataBufferFetcher dataBufferFetcher;
|
||||||
|
|
||||||
AttachmentEndpoint endpoint;
|
AttachmentEndpoint endpoint;
|
||||||
|
|
||||||
WebTestClient webClient;
|
WebTestClient webClient;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
var attachmentService = new DefaultAttachmentService(client, extensionGetter);
|
var attachmentService =
|
||||||
|
new DefaultAttachmentService(client, extensionGetter, dataBufferFetcher);
|
||||||
endpoint = new AttachmentEndpoint(attachmentService, client);
|
endpoint = new AttachmentEndpoint(attachmentService, client);
|
||||||
webClient = WebTestClient.bindToRouterFunction(endpoint.endpoint())
|
webClient = WebTestClient.bindToRouterFunction(endpoint.endpoint())
|
||||||
.apply(springSecurity())
|
.apply(springSecurity())
|
||||||
|
@ -263,4 +271,78 @@ class AttachmentEndpointTest {
|
||||||
verify(client).listBy(eq(Attachment.class), any(), any(PageRequest.class));
|
verify(client).listBy(eq(Attachment.class), any(), any(PageRequest.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ExternalTransferTest {
|
||||||
|
@Test
|
||||||
|
void shouldResponseErrorIfNoPermalinkProvided() {
|
||||||
|
webClient
|
||||||
|
.mutateWith(mockUser("fake-user").password("fake-password"))
|
||||||
|
.post()
|
||||||
|
.uri("/attachments/-/upload-from-url")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.bodyValue(Map.of("policyName", "fake-policy"))
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isBadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTransferSuccessfully() {
|
||||||
|
var policySpec = new PolicySpec();
|
||||||
|
policySpec.setConfigMapName("fake-configmap");
|
||||||
|
var policyMetadata = new Metadata();
|
||||||
|
policyMetadata.setName("fake-policy");
|
||||||
|
var policy = new Policy();
|
||||||
|
policy.setSpec(policySpec);
|
||||||
|
policy.setMetadata(policyMetadata);
|
||||||
|
|
||||||
|
var cm = new ConfigMap();
|
||||||
|
var cmMetadata = new Metadata();
|
||||||
|
cmMetadata.setName("fake-configmap");
|
||||||
|
cm.setData(Map.of());
|
||||||
|
|
||||||
|
when(client.get(Policy.class, "fake-policy")).thenReturn(Mono.just(policy));
|
||||||
|
when(client.get(ConfigMap.class, "fake-configmap")).thenReturn(Mono.just(cm));
|
||||||
|
|
||||||
|
var handler = mock(AttachmentHandler.class);
|
||||||
|
var metadata = new Metadata();
|
||||||
|
metadata.setName("fake-attachment");
|
||||||
|
var attachment = new Attachment();
|
||||||
|
attachment.setMetadata(metadata);
|
||||||
|
|
||||||
|
ResponseEntity<Void> response = new ResponseEntity<>(HttpStatusCode.valueOf(200));
|
||||||
|
DataBuffer dataBuffer = mock(DataBuffer.class);
|
||||||
|
|
||||||
|
when(handler.upload(any())).thenReturn(Mono.just(attachment));
|
||||||
|
when(dataBufferFetcher.head(any())).thenReturn(Mono.just(response));
|
||||||
|
when(dataBufferFetcher.fetch(any())).thenReturn(Flux.just(dataBuffer));
|
||||||
|
when(extensionGetter.getExtensions(AttachmentHandler.class))
|
||||||
|
.thenReturn(Flux.just(handler));
|
||||||
|
when(client.create(attachment)).thenReturn(Mono.just(attachment));
|
||||||
|
|
||||||
|
var fakeValue =
|
||||||
|
Map.of("policyName", "fake-policy", "url",
|
||||||
|
"http://localhost:8090/fake-url.jpg");
|
||||||
|
webClient
|
||||||
|
.mutateWith(mockUser("fake-user").password("fake-password"))
|
||||||
|
.post()
|
||||||
|
.uri("/attachments/-/upload-from-url")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.bodyValue(fakeValue)
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk()
|
||||||
|
.expectBody()
|
||||||
|
.jsonPath("$.metadata.name").isEqualTo("fake-attachment")
|
||||||
|
.jsonPath("$.spec.ownerName").isEqualTo("fake-user")
|
||||||
|
.jsonPath("$.spec.policyName").isEqualTo("fake-policy")
|
||||||
|
;
|
||||||
|
|
||||||
|
verify(client).get(Policy.class, "fake-policy");
|
||||||
|
verify(client).get(ConfigMap.class, "fake-configmap");
|
||||||
|
verify(client).create(attachment);
|
||||||
|
verify(dataBufferFetcher).head(any());
|
||||||
|
verify(dataBufferFetcher).fetch(any());
|
||||||
|
verify(handler).upload(any());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -320,6 +320,7 @@ models/totp-auth-link-response.ts
|
||||||
models/totp-request.ts
|
models/totp-request.ts
|
||||||
models/two-factor-auth-settings.ts
|
models/two-factor-auth-settings.ts
|
||||||
models/upgrade-from-uri-request.ts
|
models/upgrade-from-uri-request.ts
|
||||||
|
models/upload-from-url-request.ts
|
||||||
models/user-connection-list.ts
|
models/user-connection-list.ts
|
||||||
models/user-connection-spec.ts
|
models/user-connection-spec.ts
|
||||||
models/user-connection.ts
|
models/user-connection.ts
|
||||||
|
|
|
@ -25,12 +25,57 @@ import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError, ope
|
||||||
import { Attachment } from '../models';
|
import { Attachment } from '../models';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { AttachmentList } from '../models';
|
import { AttachmentList } from '../models';
|
||||||
|
// @ts-ignore
|
||||||
|
import { UploadFromUrlRequest } from '../models';
|
||||||
/**
|
/**
|
||||||
* AttachmentV1alpha1ConsoleApi - axios parameter creator
|
* AttachmentV1alpha1ConsoleApi - axios parameter creator
|
||||||
* @export
|
* @export
|
||||||
*/
|
*/
|
||||||
export const AttachmentV1alpha1ConsoleApiAxiosParamCreator = function (configuration?: Configuration) {
|
export const AttachmentV1alpha1ConsoleApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||||
return {
|
return {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {UploadFromUrlRequest} uploadFromUrlRequest
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
externalTransferAttachment: async (uploadFromUrlRequest: UploadFromUrlRequest, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'uploadFromUrlRequest' is not null or undefined
|
||||||
|
assertParamExists('externalTransferAttachment', 'uploadFromUrlRequest', uploadFromUrlRequest)
|
||||||
|
const localVarPath = `/apis/api.console.halo.run/v1alpha1/attachments/-/upload-from-url`;
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
// authentication basicAuth required
|
||||||
|
// http basic authentication required
|
||||||
|
setBasicAuthToObject(localVarRequestOptions, configuration)
|
||||||
|
|
||||||
|
// authentication bearerAuth required
|
||||||
|
// http bearer authentication required
|
||||||
|
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
localVarRequestOptions.data = serializeDataIfNeeded(uploadFromUrlRequest, localVarRequestOptions, configuration)
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} [page] Page number. Default is 0.
|
* @param {number} [page] Page number. Default is 0.
|
||||||
|
@ -178,6 +223,18 @@ export const AttachmentV1alpha1ConsoleApiAxiosParamCreator = function (configura
|
||||||
export const AttachmentV1alpha1ConsoleApiFp = function(configuration?: Configuration) {
|
export const AttachmentV1alpha1ConsoleApiFp = function(configuration?: Configuration) {
|
||||||
const localVarAxiosParamCreator = AttachmentV1alpha1ConsoleApiAxiosParamCreator(configuration)
|
const localVarAxiosParamCreator = AttachmentV1alpha1ConsoleApiAxiosParamCreator(configuration)
|
||||||
return {
|
return {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {UploadFromUrlRequest} uploadFromUrlRequest
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async externalTransferAttachment(uploadFromUrlRequest: UploadFromUrlRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Attachment>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.externalTransferAttachment(uploadFromUrlRequest, options);
|
||||||
|
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||||
|
const localVarOperationServerBasePath = operationServerMap['AttachmentV1alpha1ConsoleApi.externalTransferAttachment']?.[localVarOperationServerIndex]?.url;
|
||||||
|
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} [page] Page number. Default is 0.
|
* @param {number} [page] Page number. Default is 0.
|
||||||
|
@ -221,6 +278,15 @@ export const AttachmentV1alpha1ConsoleApiFp = function(configuration?: Configura
|
||||||
export const AttachmentV1alpha1ConsoleApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
export const AttachmentV1alpha1ConsoleApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||||
const localVarFp = AttachmentV1alpha1ConsoleApiFp(configuration)
|
const localVarFp = AttachmentV1alpha1ConsoleApiFp(configuration)
|
||||||
return {
|
return {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {AttachmentV1alpha1ConsoleApiExternalTransferAttachmentRequest} requestParameters Request parameters.
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
externalTransferAttachment(requestParameters: AttachmentV1alpha1ConsoleApiExternalTransferAttachmentRequest, options?: RawAxiosRequestConfig): AxiosPromise<Attachment> {
|
||||||
|
return localVarFp.externalTransferAttachment(requestParameters.uploadFromUrlRequest, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {AttachmentV1alpha1ConsoleApiSearchAttachmentsRequest} requestParameters Request parameters.
|
* @param {AttachmentV1alpha1ConsoleApiSearchAttachmentsRequest} requestParameters Request parameters.
|
||||||
|
@ -242,6 +308,20 @@ export const AttachmentV1alpha1ConsoleApiFactory = function (configuration?: Con
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request parameters for externalTransferAttachment operation in AttachmentV1alpha1ConsoleApi.
|
||||||
|
* @export
|
||||||
|
* @interface AttachmentV1alpha1ConsoleApiExternalTransferAttachmentRequest
|
||||||
|
*/
|
||||||
|
export interface AttachmentV1alpha1ConsoleApiExternalTransferAttachmentRequest {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {UploadFromUrlRequest}
|
||||||
|
* @memberof AttachmentV1alpha1ConsoleApiExternalTransferAttachment
|
||||||
|
*/
|
||||||
|
readonly uploadFromUrlRequest: UploadFromUrlRequest
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request parameters for searchAttachments operation in AttachmentV1alpha1ConsoleApi.
|
* Request parameters for searchAttachments operation in AttachmentV1alpha1ConsoleApi.
|
||||||
* @export
|
* @export
|
||||||
|
@ -340,6 +420,17 @@ export interface AttachmentV1alpha1ConsoleApiUploadAttachmentRequest {
|
||||||
* @extends {BaseAPI}
|
* @extends {BaseAPI}
|
||||||
*/
|
*/
|
||||||
export class AttachmentV1alpha1ConsoleApi extends BaseAPI {
|
export class AttachmentV1alpha1ConsoleApi extends BaseAPI {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {AttachmentV1alpha1ConsoleApiExternalTransferAttachmentRequest} requestParameters Request parameters.
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof AttachmentV1alpha1ConsoleApi
|
||||||
|
*/
|
||||||
|
public externalTransferAttachment(requestParameters: AttachmentV1alpha1ConsoleApiExternalTransferAttachmentRequest, options?: RawAxiosRequestConfig) {
|
||||||
|
return AttachmentV1alpha1ConsoleApiFp(this.configuration).externalTransferAttachment(requestParameters.uploadFromUrlRequest, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {AttachmentV1alpha1ConsoleApiSearchAttachmentsRequest} requestParameters Request parameters.
|
* @param {AttachmentV1alpha1ConsoleApiSearchAttachmentsRequest} requestParameters Request parameters.
|
||||||
|
|
|
@ -23,6 +23,8 @@ import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObj
|
||||||
import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
|
import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { Attachment } from '../models';
|
import { Attachment } from '../models';
|
||||||
|
// @ts-ignore
|
||||||
|
import { UploadFromUrlRequest } from '../models';
|
||||||
/**
|
/**
|
||||||
* AttachmentV1alpha1UcApi - axios parameter creator
|
* AttachmentV1alpha1UcApi - axios parameter creator
|
||||||
* @export
|
* @export
|
||||||
|
@ -87,6 +89,54 @@ export const AttachmentV1alpha1UcApiAxiosParamCreator = function (configuration?
|
||||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
localVarRequestOptions.data = localVarFormParams;
|
localVarRequestOptions.data = localVarFormParams;
|
||||||
|
|
||||||
|
return {
|
||||||
|
url: toPathString(localVarUrlObj),
|
||||||
|
options: localVarRequestOptions,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Upload attachment from the given URL.
|
||||||
|
* @param {UploadFromUrlRequest} uploadFromUrlRequest
|
||||||
|
* @param {boolean} [waitForPermalink] Wait for permalink.
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
externalTransferAttachment1: async (uploadFromUrlRequest: UploadFromUrlRequest, waitForPermalink?: boolean, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// verify required parameter 'uploadFromUrlRequest' is not null or undefined
|
||||||
|
assertParamExists('externalTransferAttachment1', 'uploadFromUrlRequest', uploadFromUrlRequest)
|
||||||
|
const localVarPath = `/apis/uc.api.content.halo.run/v1alpha1/attachments/-/upload-from-url`;
|
||||||
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
let baseOptions;
|
||||||
|
if (configuration) {
|
||||||
|
baseOptions = configuration.baseOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||||
|
const localVarHeaderParameter = {} as any;
|
||||||
|
const localVarQueryParameter = {} as any;
|
||||||
|
|
||||||
|
// authentication basicAuth required
|
||||||
|
// http basic authentication required
|
||||||
|
setBasicAuthToObject(localVarRequestOptions, configuration)
|
||||||
|
|
||||||
|
// authentication bearerAuth required
|
||||||
|
// http bearer authentication required
|
||||||
|
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||||
|
|
||||||
|
if (waitForPermalink !== undefined) {
|
||||||
|
localVarQueryParameter['waitForPermalink'] = waitForPermalink;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||||
|
|
||||||
|
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||||
|
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||||
|
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||||
|
localVarRequestOptions.data = serializeDataIfNeeded(uploadFromUrlRequest, localVarRequestOptions, configuration)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: toPathString(localVarUrlObj),
|
url: toPathString(localVarUrlObj),
|
||||||
options: localVarRequestOptions,
|
options: localVarRequestOptions,
|
||||||
|
@ -117,6 +167,19 @@ export const AttachmentV1alpha1UcApiFp = function(configuration?: Configuration)
|
||||||
const localVarOperationServerBasePath = operationServerMap['AttachmentV1alpha1UcApi.createAttachmentForPost']?.[localVarOperationServerIndex]?.url;
|
const localVarOperationServerBasePath = operationServerMap['AttachmentV1alpha1UcApi.createAttachmentForPost']?.[localVarOperationServerIndex]?.url;
|
||||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Upload attachment from the given URL.
|
||||||
|
* @param {UploadFromUrlRequest} uploadFromUrlRequest
|
||||||
|
* @param {boolean} [waitForPermalink] Wait for permalink.
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
async externalTransferAttachment1(uploadFromUrlRequest: UploadFromUrlRequest, waitForPermalink?: boolean, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Attachment>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.externalTransferAttachment1(uploadFromUrlRequest, waitForPermalink, options);
|
||||||
|
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||||
|
const localVarOperationServerBasePath = operationServerMap['AttachmentV1alpha1UcApi.externalTransferAttachment1']?.[localVarOperationServerIndex]?.url;
|
||||||
|
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -136,6 +199,15 @@ export const AttachmentV1alpha1UcApiFactory = function (configuration?: Configur
|
||||||
createAttachmentForPost(requestParameters: AttachmentV1alpha1UcApiCreateAttachmentForPostRequest, options?: RawAxiosRequestConfig): AxiosPromise<Attachment> {
|
createAttachmentForPost(requestParameters: AttachmentV1alpha1UcApiCreateAttachmentForPostRequest, options?: RawAxiosRequestConfig): AxiosPromise<Attachment> {
|
||||||
return localVarFp.createAttachmentForPost(requestParameters.file, requestParameters.waitForPermalink, requestParameters.postName, requestParameters.singlePageName, options).then((request) => request(axios, basePath));
|
return localVarFp.createAttachmentForPost(requestParameters.file, requestParameters.waitForPermalink, requestParameters.postName, requestParameters.singlePageName, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Upload attachment from the given URL.
|
||||||
|
* @param {AttachmentV1alpha1UcApiExternalTransferAttachment1Request} requestParameters Request parameters.
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
externalTransferAttachment1(requestParameters: AttachmentV1alpha1UcApiExternalTransferAttachment1Request, options?: RawAxiosRequestConfig): AxiosPromise<Attachment> {
|
||||||
|
return localVarFp.externalTransferAttachment1(requestParameters.uploadFromUrlRequest, requestParameters.waitForPermalink, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -174,6 +246,27 @@ export interface AttachmentV1alpha1UcApiCreateAttachmentForPostRequest {
|
||||||
readonly singlePageName?: string
|
readonly singlePageName?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request parameters for externalTransferAttachment1 operation in AttachmentV1alpha1UcApi.
|
||||||
|
* @export
|
||||||
|
* @interface AttachmentV1alpha1UcApiExternalTransferAttachment1Request
|
||||||
|
*/
|
||||||
|
export interface AttachmentV1alpha1UcApiExternalTransferAttachment1Request {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {UploadFromUrlRequest}
|
||||||
|
* @memberof AttachmentV1alpha1UcApiExternalTransferAttachment1
|
||||||
|
*/
|
||||||
|
readonly uploadFromUrlRequest: UploadFromUrlRequest
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for permalink.
|
||||||
|
* @type {boolean}
|
||||||
|
* @memberof AttachmentV1alpha1UcApiExternalTransferAttachment1
|
||||||
|
*/
|
||||||
|
readonly waitForPermalink?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AttachmentV1alpha1UcApi - object-oriented interface
|
* AttachmentV1alpha1UcApi - object-oriented interface
|
||||||
* @export
|
* @export
|
||||||
|
@ -191,5 +284,16 @@ export class AttachmentV1alpha1UcApi extends BaseAPI {
|
||||||
public createAttachmentForPost(requestParameters: AttachmentV1alpha1UcApiCreateAttachmentForPostRequest, options?: RawAxiosRequestConfig) {
|
public createAttachmentForPost(requestParameters: AttachmentV1alpha1UcApiCreateAttachmentForPostRequest, options?: RawAxiosRequestConfig) {
|
||||||
return AttachmentV1alpha1UcApiFp(this.configuration).createAttachmentForPost(requestParameters.file, requestParameters.waitForPermalink, requestParameters.postName, requestParameters.singlePageName, options).then((request) => request(this.axios, this.basePath));
|
return AttachmentV1alpha1UcApiFp(this.configuration).createAttachmentForPost(requestParameters.file, requestParameters.waitForPermalink, requestParameters.postName, requestParameters.singlePageName, options).then((request) => request(this.axios, this.basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload attachment from the given URL.
|
||||||
|
* @param {AttachmentV1alpha1UcApiExternalTransferAttachment1Request} requestParameters Request parameters.
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
* @memberof AttachmentV1alpha1UcApi
|
||||||
|
*/
|
||||||
|
public externalTransferAttachment1(requestParameters: AttachmentV1alpha1UcApiExternalTransferAttachment1Request, options?: RawAxiosRequestConfig) {
|
||||||
|
return AttachmentV1alpha1UcApiFp(this.configuration).externalTransferAttachment1(requestParameters.uploadFromUrlRequest, requestParameters.waitForPermalink, options).then((request) => request(this.axios, this.basePath));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -236,6 +236,7 @@ export * from './totp-auth-link-response';
|
||||||
export * from './totp-request';
|
export * from './totp-request';
|
||||||
export * from './two-factor-auth-settings';
|
export * from './two-factor-auth-settings';
|
||||||
export * from './upgrade-from-uri-request';
|
export * from './upgrade-from-uri-request';
|
||||||
|
export * from './upload-from-url-request';
|
||||||
export * from './user';
|
export * from './user';
|
||||||
export * from './user-connection';
|
export * from './user-connection';
|
||||||
export * from './user-connection-list';
|
export * from './user-connection-list';
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* Halo
|
||||||
|
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||||
|
*
|
||||||
|
* The version of the OpenAPI document: 2.19.0-SNAPSHOT
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
* https://openapi-generator.tech
|
||||||
|
* Do not edit the class manually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @interface UploadFromUrlRequest
|
||||||
|
*/
|
||||||
|
export interface UploadFromUrlRequest {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof UploadFromUrlRequest
|
||||||
|
*/
|
||||||
|
'filename'?: string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof UploadFromUrlRequest
|
||||||
|
*/
|
||||||
|
'url': string;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue