Merge pull request #526 from lowdefy/s3-upload

Fix S3UploadButton
This commit is contained in:
Johann Möller 2021-03-31 11:52:06 +02:00 committed by GitHub
commit 06d35a157d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 185 additions and 98 deletions

View File

@ -33,48 +33,51 @@ const getCustomRequest = ({ methods, setS3Parameters }) => async ({
onProgress,
onSuccess,
}) => {
const { name, size, type, uid } = file;
try {
const { name, size, type, uid } = file;
const s3PostPolicyResponse = await methods.triggerEvent({
name: '__getS3PostPolicy',
event: { filename: name, size, type, uid },
});
const s3PostPolicyResponse = await methods.triggerEvent({
name: '__getS3PostPolicy',
event: { filename: name, size, type, uid },
});
if (s3PostPolicyResponse[0].error) {
onError(s3PostPolicyResponse[0].error);
return;
}
const { url, fields } = s3PostPolicyResponse[0].response;
const { bucket, key } = fields;
setS3Parameters((prevState) => {
const ret = { ...prevState };
ret[uid] = { bucket, key };
return ret;
});
// Set 20 % progress on policy is acquired else user waits to long before progress is reported
onProgress({ percent: 20 });
// Create FormData with all required fields in S3 policy
const formData = new FormData();
Object.keys(fields).forEach((field) => {
formData.append(field, fields[field]);
});
// file needs to be the last field in the form
formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
onProgress({ percent: (event.loaded / event.total) * 80 + 20 });
if (s3PostPolicyResponse.success !== true) {
throw new Error('S3 post policy request error.');
}
};
xhr.addEventListener('error', onError);
xhr.addEventListener('load', onSuccess);
xhr.open('post', url);
xhr.send(formData);
const { url, fields } = s3PostPolicyResponse.responses[0].response[0];
const { bucket, key } = fields;
setS3Parameters((prevState) => {
const ret = { ...prevState };
ret[uid] = { bucket, key };
return ret;
});
// Set 20 % progress on policy is acquired else user waits to long before progress is reported
onProgress({ percent: 20 });
// Create FormData with all required fields in S3 policy
const formData = new FormData();
Object.keys(fields).forEach((field) => {
formData.append(field, fields[field]);
});
// file needs to be the last field in the form
formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (event) => {
if (event.lengthComputable) {
onProgress({ percent: (event.loaded / event.total) * 80 + 20 });
}
};
xhr.addEventListener('error', onError);
xhr.addEventListener('load', onSuccess);
xhr.open('post', url);
xhr.send(formData);
} catch (error) {
onError(error);
}
};
const S3UploadButtonBlock = ({ blockId, events, methods, properties, value }) => {
@ -83,7 +86,7 @@ const S3UploadButtonBlock = ({ blockId, events, methods, properties, value }) =>
// so it cannot set the value directly. customRequest sets the parameters to s3Parameters state,
// and then onChange updates the block value.
const [s3Parameters, setS3Parameters] = useState(value);
let customRequest;
const customRequest = getCustomRequest({ methods, setS3Parameters });
useEffect(() => {
methods.setValue({ file: null, fileList: [] });
methods.registerEvent({
@ -92,11 +95,10 @@ const S3UploadButtonBlock = ({ blockId, events, methods, properties, value }) =>
{
id: `${blockId}__getS3PostPolicy`,
type: 'Request',
params: properties.s3PostPolicyRequestId,
params: [properties.s3PostPolicyRequestId],
},
],
});
customRequest = getCustomRequest({ methods, setS3Parameters });
}, []);
const disabled = getDisabled({ properties, value });

View File

@ -13,7 +13,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "default__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -48,7 +50,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": false,
"id": "default",
"multiple": true,
@ -68,7 +70,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "default__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -103,7 +107,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": false,
"id": "default",
"multiple": true,
@ -128,7 +132,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "properties.accept__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -163,7 +169,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": false,
"id": "properties.accept",
"multiple": true,
@ -183,7 +189,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "properties.accept__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -218,7 +226,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": false,
"id": "properties.accept",
"multiple": true,
@ -243,7 +251,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "properties.button__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -278,7 +288,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": false,
"id": "properties.button",
"multiple": true,
@ -298,7 +308,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "properties.button__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -333,7 +345,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": false,
"id": "properties.button",
"multiple": true,
@ -358,7 +370,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "properties.disabled__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -393,7 +407,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": true,
"id": "properties.disabled",
"multiple": true,
@ -413,7 +427,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "properties.disabled__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -448,7 +464,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": true,
"id": "properties.disabled",
"multiple": true,
@ -473,7 +489,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "properties.showUploadList__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -508,7 +526,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": false,
"id": "properties.showUploadList",
"multiple": true,
@ -528,7 +546,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "properties.showUploadList__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -563,7 +583,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": false,
"id": "properties.showUploadList",
"multiple": true,
@ -588,7 +608,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "properties.singleFile__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -623,7 +645,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": false,
"id": "properties.singleFile",
"multiple": false,
@ -643,7 +665,9 @@ Array [
"__getS3PostPolicy": Array [
Object {
"id": "properties.singleFile__getS3PostPolicy",
"params": "s3PostPolicyRequestId",
"params": Array [
"s3PostPolicyRequestId",
],
"type": "Request",
},
],
@ -678,7 +702,7 @@ Array [
}
}
/>,
"customRequest": undefined,
"customRequest": [Function],
"disabled": false,
"id": "properties.singleFile",
"multiple": false,

View File

@ -74,14 +74,14 @@ class Requests {
return this.fetch({ requestId, event, arrayIndices });
}
fetch({ requestId, event, arrayIndices }) {
async fetch({ requestId, event, arrayIndices }) {
this.context.requests[requestId].loading = true;
if (this.context.RootBlocks) {
this.context.RootBlocks.setBlocksLoadingCache();
}
return this.context.lowdefy.client
.query({
try {
const gqlResponse = await this.context.lowdefy.client.query({
query: CALL_REQUEST,
fetchPolicy: 'network-only',
variables: {
@ -97,24 +97,22 @@ class Requests {
urlQuery: serializer.serialize(this.context.lowdefy.urlQuery),
},
},
})
.then((result) => {
this.context.requests[requestId].response = serializer.deserialize(
get(result, 'data.request.response', {
default: null,
})
);
this.context.requests[requestId].error.unshift(null);
return result;
})
.catch((error) => {
this.context.requests[requestId].error.unshift(error);
throw error;
})
.finally(() => {
this.context.requests[requestId].loading = false;
this.context.update();
});
const response = serializer.deserialize(
get(gqlResponse, 'data.request.response', {
default: null,
})
);
this.context.requests[requestId].response = response;
this.context.requests[requestId].loading = false;
this.context.update();
return response;
} catch (error) {
this.context.requests[requestId].error.unshift(error);
this.context.requests[requestId].loading = false;
this.context.update();
throw error;
}
}
}

View File

@ -132,12 +132,28 @@ test('Request call one request', async () => {
loading: true,
response: null,
});
await promise;
const res = await promise;
expect(context.requests.req_one).toEqual({
error: [null],
error: [],
loading: false,
response: 1,
});
expect(res).toEqual({
blockId: 'button',
event: undefined,
eventName: 'onClick',
responses: [
{
actionId: 'a',
actionType: 'Request',
response: [1],
},
],
success: true,
timestamp: {
date: 0,
},
});
});
test('Request call all requests', async () => {
@ -190,19 +206,35 @@ test('Request call all requests', async () => {
response: null,
},
});
await promise;
const res = await promise;
expect(context.requests).toEqual({
req_one: {
error: [null],
error: [],
loading: false,
response: 1,
},
req_two: {
error: [null],
error: [],
loading: false,
response: 2,
},
});
expect(res).toEqual({
blockId: 'button',
event: undefined,
eventName: 'onClick',
responses: [
{
actionId: 'a',
actionType: 'Request',
response: [1, 2],
},
],
success: true,
timestamp: {
date: 0,
},
});
});
test('Request call array of requests', async () => {
@ -255,19 +287,35 @@ test('Request call array of requests', async () => {
response: null,
},
});
await promise;
const res = await promise;
expect(context.requests).toEqual({
req_one: {
error: [null],
error: [],
loading: false,
response: 1,
},
req_two: {
error: [null],
error: [],
loading: false,
response: 2,
},
});
expect(res).toEqual({
blockId: 'button',
event: undefined,
eventName: 'onClick',
responses: [
{
actionId: 'a',
actionType: 'Request',
response: [1, 2],
},
],
success: true,
timestamp: {
date: 0,
},
});
});
test('Request pass if params are none', async () => {

View File

@ -93,7 +93,7 @@ test('callRequest', async () => {
await context.Requests.callRequest({ requestId: 'req_one' });
expect(context.requests).toEqual({
req_one: {
error: [null],
error: [],
loading: false,
response: 1,
},
@ -156,7 +156,7 @@ test('callRequests all requests', async () => {
}
expect(context.requests).toEqual({
req_one: {
error: [null],
error: [],
loading: false,
response: 1,
},
@ -166,7 +166,7 @@ test('callRequests all requests', async () => {
response: null,
},
req_watch: {
error: [null],
error: [],
loading: false,
response: 2,
},
@ -190,7 +190,7 @@ test('callRequests', async () => {
await promise;
expect(context.requests).toEqual({
req_one: {
error: [null],
error: [],
loading: false,
response: 1,
},
@ -269,6 +269,21 @@ test('update function should be called', async () => {
expect(updateFunction).toHaveBeenCalledTimes(1);
});
test('update function should be called if error', async () => {
const updateFunction = jest.fn();
const context = testContext({
lowdefy,
rootBlock,
});
context.update = updateFunction;
try {
await context.Requests.callRequest({ requestId: 'req_error' });
} catch (e) {
// catch thrown errors
}
expect(updateFunction).toHaveBeenCalledTimes(1);
});
test('fetch should set blocks loading and call query every time it is called', async () => {
const context = testContext({
lowdefy,