Skip to content

Commit

Permalink
feat: sdk storage support (refinedev#2522)
Browse files Browse the repository at this point in the history
* feat(sdk): upload and getPublicUrl support

* chore(cloud-example): add upload example

* chore(sdk): changeset

* chore(sdk): refactor applicationClientId data
  • Loading branch information
yildirayunlu authored Sep 16, 2022
1 parent 5d21008 commit e0dce4d
Show file tree
Hide file tree
Showing 10 changed files with 246 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/happy-monkeys-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@pankod/refine-sdk": minor
---

Add storage support
57 changes: 56 additions & 1 deletion examples/cloud/src/pages/posts/create.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import React, { useState } from "react";
import { IResourceComponentsProps } from "@pankod/refine-core";
import { Create, Form, Input, Select } from "@pankod/refine-antd";
import {
Create,
Form,
Input,
RcFile,
Select,
Upload,
} from "@pankod/refine-antd";
import { useForm, useSelect } from "@pankod/refine-antd";
import ReactMarkdown from "react-markdown";
import ReactMde from "react-mde";
import "react-mde/lib/styles/css/react-mde-all.css";
import { useSdk } from "@pankod/refine-cloud";

import { normalizeFile } from "utility/normalize";
import { IPost, ICategory } from "interfaces";

export const PostCreate: React.FC<IResourceComponentsProps> = () => {
const { formProps, saveButtonProps } = useForm<IPost>();
const { sdk } = useSdk();

const { selectProps: categorySelectProps } = useSelect<ICategory>({
resource: "categories",
Expand Down Expand Up @@ -88,6 +98,51 @@ export const PostCreate: React.FC<IResourceComponentsProps> = () => {
}
/>
</Form.Item>
<Form.Item label="Images">
<Form.Item
name="images"
valuePropName="fileList"
normalize={normalizeFile}
noStyle
>
<Upload.Dragger
name="file"
listType="picture"
multiple
customRequest={async ({
file,
onError,
onSuccess,
}) => {
const bucket = "test1";
try {
await sdk.storage.upload({
bucket,
file,
});

// get uploaded file url
const path = (file as RcFile).name;
const { url } =
await sdk.storage.getPublicUrl({
bucket,
path,
});

const xhr = new XMLHttpRequest();
onSuccess && onSuccess({ url }, xhr);
} catch (error) {
onError &&
onError(new Error("Upload Error"));
}
}}
>
<p className="ant-upload-text">
Drag & drop a file in this area
</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Create>
);
Expand Down
63 changes: 62 additions & 1 deletion examples/cloud/src/pages/posts/edit.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
import React, { useState } from "react";
import { IResourceComponentsProps } from "@pankod/refine-core";
import { Edit, Form, Input, Select, Row, Col } from "@pankod/refine-antd";
import {
Edit,
Form,
Input,
Select,
Row,
Col,
Upload,
RcFile,
} from "@pankod/refine-antd";
import { useForm, useSelect } from "@pankod/refine-antd";
import { LogList } from "@pankod/refine-antd-audit-log";
import ReactMarkdown from "react-markdown";
import { useSdk } from "@pankod/refine-cloud";
import ReactMde from "react-mde";
import "react-mde/lib/styles/css/react-mde-all.css";

import { normalizeFile } from "utility/normalize";
import { IPost, ICategory } from "interfaces";

export const PostEdit: React.FC<IResourceComponentsProps> = () => {
const { sdk } = useSdk();

const { formProps, saveButtonProps, queryResult } = useForm<IPost>({
warnWhenUnsavedChanges: true,
});
Expand Down Expand Up @@ -97,6 +110,54 @@ export const PostEdit: React.FC<IResourceComponentsProps> = () => {
}
/>
</Form.Item>
<Form.Item label="Images">
<Form.Item
name="images"
valuePropName="fileList"
normalize={normalizeFile}
noStyle
>
<Upload.Dragger
name="file"
listType="picture"
multiple
customRequest={async ({
file,
onError,
onSuccess,
}) => {
const bucket = "test1";
try {
await sdk.storage.upload({
bucket,
file,
});

// get uploaded file url
const path = (file as RcFile).name;
const { url } =
await sdk.storage.getPublicUrl({
bucket,
path,
});

const xhr = new XMLHttpRequest();
onSuccess &&
onSuccess({ url }, xhr);
} catch (error) {
onError &&
onError(
new Error("Upload Error"),
);
}
}}
>
<p className="ant-upload-text">
Drag & drop a file in this area
</p>
</Upload.Dragger>
</Form.Item>
</Form.Item>
</Form>
</Edit>
</Col>
Expand Down
27 changes: 27 additions & 0 deletions examples/cloud/src/utility/normalize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { UploadFile } from "@pankod/refine-antd";

interface UploadResponse {
url: string;
}
interface EventArgs<T = UploadResponse> {
file: UploadFile<T>;
fileList: Array<UploadFile<T>>;
}

export const normalizeFile = (event: EventArgs) => {
const { fileList } = event;

return fileList.map((item) => {
const { uid, name, type, size, response, percent, status } = item;

return {
uid,
name,
url: item.url || response?.url,
type,
size,
percent,
status,
};
});
};
21 changes: 13 additions & 8 deletions packages/sdk/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ import createAuthRefreshInterceptor, {
AxiosAuthRefreshRequestConfig,
} from "axios-auth-refresh";

import { Auth, Log, DraftResource, Config, CloudQuery } from "./services";
import {
Auth,
Log,
DraftResource,
Config,
CloudQuery,
Storage,
} from "./services";

class Client {
private baseUrl: string;
Expand Down Expand Up @@ -137,6 +144,10 @@ class Client {
return new CloudQuery(this);
}

get storage(): Storage {
return new Storage(this);
}

async call<D>(payload: {
method:
| "get"
Expand All @@ -152,13 +163,7 @@ class Client {
skipAuthRefresh?: boolean;
headers?: AxiosRequestHeaders;
}): Promise<D> {
const { params, method, url, skipAuthRefresh, headers } = payload;
let { data } = payload;

data = {
...data,
applicationClientId: this.clientId,
};
const { params, method, url, skipAuthRefresh, headers, data } = payload;

const config: AxiosAuthRefreshRequestConfig = {
baseURL: this.baseUrl,
Expand Down
6 changes: 5 additions & 1 deletion packages/sdk/src/services/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class Auth {
data: {
email,
password,
applicationClientId: this.client.getClientId(),
},
});
}
Expand All @@ -45,7 +46,10 @@ class Auth {
return await this.client.call({
method: "post",
url: "/auth/register",
data,
data: {
...data,
applicationClientId: this.client.getClientId(),
},
});
}

Expand Down
5 changes: 4 additions & 1 deletion packages/sdk/src/services/draft-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ class DraftResource {
return await this.client.call({
method: "post",
url: "/draft-resource/client",
data,
data: {
...data,
applicationClientId: this.client.getClientId(),
},
headers: {
"x-refine-cloud-token": refineCloudToken || "",
},
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./log";
export * from "./draft-resource";
export * from "./config";
export * from "./cloud-query";
export * from "./storage";
10 changes: 8 additions & 2 deletions packages/sdk/src/services/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ class Log {
return await this.client.call({
method: "post",
url: "/log/client",
data,
data: {
...data,
applicationClientId: this.client.getClientId(),
},
});
}

Expand Down Expand Up @@ -58,7 +61,10 @@ class Log {
return await this.client.call({
method: "patch",
url: `/log/client/${id}`,
data,
data: {
...data,
applicationClientId: this.client.getClientId(),
},
});
}
}
Expand Down
65 changes: 65 additions & 0 deletions packages/sdk/src/services/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Client } from "../client";

class Storage {
private client: Client;

constructor(client: Client) {
this.client = client;
}

async getPublicUrl(payload: {
bucket: string;
path: string;
}): Promise<{ url: string }> {
const { bucket, path } = payload;

return await this.client.call({
method: "post",
url: `/storage/generate-public-url/${bucket}`,
data: {
path,
},
});
}

async upload(payload: {
bucket: string;
file:
| ArrayBuffer
| ArrayBufferView
| Blob
| Buffer
| File
| FormData
| NodeJS.ReadableStream
| ReadableStream<Uint8Array>
| URLSearchParams
| string;
}): Promise<any> {
const { bucket, file } = payload;

let body;
if (typeof Blob !== "undefined" && file instanceof Blob) {
body = new FormData();
body.append("file", file);
} else if (
typeof FormData !== "undefined" &&
file instanceof FormData
) {
body = file;
} else {
body = file;
}

return await this.client.call({
method: "post",
url: `/storage/upload/${bucket}`,
data: body,
headers: {
"Content-Type": "multipart/form-data",
},
});
}
}

export { Storage };

0 comments on commit e0dce4d

Please sign in to comment.