Skip to content

Commit

Permalink
Merge pull request #1471 from polywrap/http-plugin-formdata
Browse files Browse the repository at this point in the history
http plugin form-data support
  • Loading branch information
dOrgJelli authored Jan 11, 2023
2 parents 2b39095 + 9730e3f commit c1ad9c9
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 9 deletions.
18 changes: 18 additions & 0 deletions packages/interfaces/http/src/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,28 @@ type Request {
headers: Map @annotate(type: "Map<String!, String!>")
urlParams: Map @annotate(type: "Map<String!, String!>")
responseType: ResponseType!
"""The body of the request. If present, the `formData` property will be ignored."""
body: String
"""
An alternative to the standard request body, 'formData' is expected to be in the 'multipart/form-data' format.
If present, the `body` property is not null, `formData` will be ignored.
Otherwise, if formData is not null, the following header will be added to the request: 'Content-Type: multipart/form-data'.
"""
formData: [FormDataEntry!]
timeout: UInt32
}

type FormDataEntry {
"""FormData entry key"""
name: String!
"""If 'type' is defined, value is treated as a base64 byte string"""
value: String
"""File name to report to the server"""
fileName: String
"""MIME type (https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types). Defaults to empty string."""
type: String
}

enum ResponseType {
TEXT
BINARY
Expand Down
4 changes: 3 additions & 1 deletion packages/js/plugins/http/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@
"dependencies": {
"@polywrap/core-js": "0.10.0-pre.6",
"@polywrap/plugin-js": "0.10.0-pre.6",
"axios": "0.21.4"
"axios": "0.21.4",
"form-data": "4.0.0"
},
"devDependencies": {
"@polywrap/client-js": "0.10.0-pre.6",
"@polywrap/fs-plugin-js": "0.10.0-pre.6",
"@polywrap/fs-resolver-plugin-js": "0.10.0-pre.6",
"@polywrap/uri-resolver-extensions-js": "0.10.0-pre.6",
"@polywrap/uri-resolvers-js": "0.10.0-pre.6",
"@polywrap/test-env-js": "0.10.0-pre.6",
"@types/jest": "26.0.8",
"@types/prettier": "2.6.0",
"jest": "26.6.3",
Expand Down
93 changes: 93 additions & 0 deletions packages/js/plugins/http/src/__tests__/e2e/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { UriResolver } from "@polywrap/uri-resolvers-js";

import nock from "nock";
import { WrapError } from "@polywrap/core-js";
import { initTestEnvironment, stopTestEnvironment, providers } from "@polywrap/test-env-js";

jest.setTimeout(360000);

Expand All @@ -17,6 +18,10 @@ const defaultReplyHeaders = {
describe("e2e tests for HttpPlugin", () => {
let polywrapClient: PolywrapClient;

beforeAll(async () => {
await initTestEnvironment();
});

beforeEach(() => {
polywrapClient = new PolywrapClient(
{
Expand All @@ -29,6 +34,10 @@ describe("e2e tests for HttpPlugin", () => {
);
});

afterAll(async () => {
await stopTestEnvironment();
});

describe("get method", () => {
test("successful request with response type as TEXT", async () => {
nock("http://www.example.com")
Expand Down Expand Up @@ -288,5 +297,89 @@ describe("e2e tests for HttpPlugin", () => {
expect(response.error).toBeDefined();
expect(response.ok).toBeFalsy();
});

test("successful request with form-data (simple)", async () => {
const response = await polywrapClient.invoke<Http_Response>({
uri: "wrap://ens/http.polywrap.eth",
method: "post",
args: {
url: `${providers.ipfs}/api/v0/add`,
request: {
responseType: "TEXT",
formData:[{
name:"test.txt",
value:"QSBuZXcgc2FtcGxlIGZpbGU=",
fileName:"test.txt",
type:"application/octet-stream"
}],
},
},
});

if (!response.ok) fail(response.error);
expect(response.value).toBeDefined();
expect(response.value?.status).toBe(200);
expect(response.value?.body).toBe(JSON.stringify({
Name: "test.txt",
Hash: "Qmawvzw32Jq7RbMw2K8axEbzfNK74NPynBoq4tJnWvkYqP",
Size: "25"
}));
});

test("successful request with form-data (complex)", async () => {
const response = await polywrapClient.invoke<Http_Response>({
uri: "wrap://ens/http.polywrap.eth",
method: "post",
args: {
url: `${providers.ipfs}/api/v0/add`,
request: {
responseType: "TEXT",
formData:[
{ name: "file_0.txt", value: "ZmlsZV8w", fileName: "file_0.txt", type: "application/octet-stream" },
{ name: "file_1.txt", value: "ZmlsZV8x",fileName: "file_1.txt", type: "application/octet-stream" },
{ name: "directory_A", value: null, fileName: "directory_A", type: "application/x-directory" },
{ name: "directory_A/file_A_0.txt", value: "ZmlsZV9BXzA=", fileName: "directory_A%2Ffile_A_0.txt", type: "application/octet-stream" },
{ name: "directory_A/file_A_1.txt", value: "ZmlsZV9BXzE=", fileName: "directory_A%2Ffile_A_1.txt", type: "application/octet-stream" }
],
},
},
});

if (!response.ok) fail(response.error);
expect(response.value).toBeDefined();
expect(response.value?.status).toBe(200);

const results = response.value?.body?.trim()
.split("\n")
.map((v) => JSON.parse(v));

expect(results).toStrictEqual([
{
Name: "file_0.txt",
Hash: "QmV3uDt3KhEYchouUzEbfz7FBA2c2LvNo76dxLLwJW76b1",
Size: "14"
},
{
Name: "file_1.txt",
Hash: "QmYwMByE4ibjuMu2nRYRfBweJGJErjmMXfZ92srKhYfq5f",
Size: "14"
},
{
Name: "directory_A/file_A_0.txt",
Hash: "QmeYp73qnn8EdogE4d6BhQCHtep7dkRC8FgdE3Qbo4nY9c",
Size: "16"
},
{
Name: "directory_A/file_A_1.txt",
Hash: "QmWetZjwHWuGsDyxX6ae5wGS68mFTXC5x61H1TUNxqBXzn",
Size: "16"
},
{
Name: "directory_A",
Hash: "Qmb5XsySizDeTn1kvNbyiiNy9eyg3Lb6EwGjQt7iiKBxoL",
Size: "144"
},
]);
});
});
});
29 changes: 22 additions & 7 deletions packages/js/plugins/http/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import {
Http_Response,
manifest,
} from "./wrap";
import { fromAxiosResponse, toAxiosRequestConfig } from "./util";
import { fromAxiosResponse, toAxiosRequestConfig, toFormData } from "./util";

import axios from "axios";
import axios, { AxiosResponse } from "axios";
import { PluginFactory, PluginPackage } from "@polywrap/plugin-js";

type NoConfig = Record<string, never>;
Expand All @@ -29,11 +29,26 @@ export class HttpPlugin extends Module<NoConfig> {
args: Args_post,
_client: CoreClient
): Promise<Http_Response | null> {
const response = await axios.post(
args.url,
args.request ? args.request.body : undefined,
args.request ? toAxiosRequestConfig(args.request) : undefined
);
let response: AxiosResponse;
if (args.request?.body) {
response = await axios.post(
args.url,
args.request.body,
toAxiosRequestConfig(args.request)
);
} else if (args.request?.formData) {
const data = toFormData(args.request.formData);
const config = toAxiosRequestConfig(args.request);
config.headers = {
...(config.headers as Record<string, unknown>),
...data.getHeaders(),
};
response = await axios.post(args.url, data, config);
} else if (args.request) {
response = await axios.post(args.url, toAxiosRequestConfig(args.request));
} else {
response = await axios.post(args.url);
}
return fromAxiosResponse(response);
}
}
Expand Down
27 changes: 26 additions & 1 deletion packages/js/plugins/http/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { Http_Request, Http_Response, Http_ResponseTypeEnum } from "./wrap";
import {
Http_Request,
Http_Response,
Http_ResponseTypeEnum,
Http_FormDataEntry,
} from "./wrap";

import { AxiosResponse, AxiosRequestConfig } from "axios";
import FormData from "form-data";

/**
* Convert AxiosResponse<string> to Response
Expand Down Expand Up @@ -90,3 +96,22 @@ export function toAxiosRequestConfig(

return config;
}

export function toFormData(entries: Http_FormDataEntry[]): FormData {
const fd = new FormData();
entries.forEach((entry) => {
const options: FormData.AppendOptions = {};
options.contentType = entry.type ?? undefined;
options.filename = entry.fileName ?? undefined;
let value: string | Buffer | undefined;
if (entry.type) {
value = entry.value
? Buffer.from(entry.value, "base64")
: Buffer.alloc(0);
} else {
value = entry.value ?? undefined;
}
fd.append(entry.name, value, options);
});
return fd;
}

0 comments on commit c1ad9c9

Please sign in to comment.