mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-02-23 14:39:32 +08:00
feat(graphql): add google sheet delete requests
This commit is contained in:
parent
4d5dbb2151
commit
00dd897034
@ -17,6 +17,8 @@
|
||||
import schema from './GoogleSheetSchema.json';
|
||||
import GoogleSheetAppendMany from './GoogleSheetAppendMany/GoogleSheetAppendMany';
|
||||
import GoogleSheetAppendOne from './GoogleSheetAppendOne/GoogleSheetAppendOne';
|
||||
import GoogleSheetDeleteOne from './GoogleSheetDeleteOne/GoogleSheetDeleteOne';
|
||||
import GoogleSheetDeleteMany from './GoogleSheetDeleteMany/GoogleSheetDeleteMany';
|
||||
import GoogleSheetGetMany from './GoogleSheetGetMany/GoogleSheetGetMany';
|
||||
import GoogleSheetGetOne from './GoogleSheetGetOne/GoogleSheetGetOne';
|
||||
|
||||
@ -25,6 +27,8 @@ export default {
|
||||
requests: {
|
||||
GoogleSheetAppendMany,
|
||||
GoogleSheetAppendOne,
|
||||
GoogleSheetDeleteOne,
|
||||
GoogleSheetDeleteMany,
|
||||
GoogleSheetGetMany,
|
||||
GoogleSheetGetOne,
|
||||
},
|
||||
|
@ -21,7 +21,10 @@ import { ConfigurationError } from '../../context/errors';
|
||||
const { schema } = GoogleSheet;
|
||||
|
||||
test('All requests are present', () => {
|
||||
expect(GoogleSheet.requests.GoogleSheetAppendOne).toBeDefined();
|
||||
expect(GoogleSheet.requests.GoogleSheetAppendMany).toBeDefined();
|
||||
expect(GoogleSheet.requests.GoogleSheetDeleteOne).toBeDefined();
|
||||
expect(GoogleSheet.requests.GoogleSheetDeleteMany).toBeDefined();
|
||||
expect(GoogleSheet.requests.GoogleSheetGetMany).toBeDefined();
|
||||
expect(GoogleSheet.requests.GoogleSheetGetOne).toBeDefined();
|
||||
});
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright 2020 Lowdefy, Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import schema from './GoogleSheetDeleteManySchema.json';
|
||||
import getSheet from '../getSheet';
|
||||
import mingoFilter from '../../../utils/mingoFilter';
|
||||
|
||||
async function googleSheetDeleteMany({ request, connection }) {
|
||||
const { filter, options = {} } = request;
|
||||
const { limit, skip } = options;
|
||||
const sheet = await getSheet({ connection });
|
||||
let rows = await sheet.getRows({ limit, offset: skip });
|
||||
rows = mingoFilter({ input: rows, filter });
|
||||
if (rows.length === 0) {
|
||||
return {
|
||||
deletedCount: 0,
|
||||
};
|
||||
}
|
||||
const promises = rows.map(async (row) => {
|
||||
await row.delete();
|
||||
});
|
||||
await Promise.all(promises);
|
||||
return {
|
||||
deletedCount: rows.length,
|
||||
};
|
||||
}
|
||||
|
||||
export default { resolver: googleSheetDeleteMany, schema, checkRead: false, checkWrite: true };
|
@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright 2020 Lowdefy, Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import GoogleSheetDeleteMany from './GoogleSheetDeleteMany';
|
||||
import { ConfigurationError } from '../../../context/errors';
|
||||
import testSchema from '../../../utils/testSchema';
|
||||
|
||||
const mockGetRows = jest.fn();
|
||||
const mockDelete = jest.fn();
|
||||
jest.mock('../getSheet', () => () => ({
|
||||
getRows: mockGetRows,
|
||||
}));
|
||||
|
||||
const mockGetRowsDefaultImp = ({ limit, offset }) => {
|
||||
const rows = [
|
||||
{
|
||||
_rowNumber: 2,
|
||||
_rawData: ['1', 'John', '34', '2020/04/26', 'TRUE'],
|
||||
id: '1',
|
||||
name: 'John',
|
||||
age: '34',
|
||||
birth_date: '2020/04/26',
|
||||
married: 'TRUE',
|
||||
_sheet: {},
|
||||
delete: mockDelete,
|
||||
},
|
||||
{
|
||||
_rowNumber: 3,
|
||||
_rawData: ['2', 'Steven', '43', '2020/04/27', 'FALSE'],
|
||||
id: '2',
|
||||
name: 'Steve',
|
||||
age: '43',
|
||||
birth_date: '2020/04/27',
|
||||
married: 'FALSE',
|
||||
_sheet: {},
|
||||
delete: mockDelete,
|
||||
},
|
||||
{
|
||||
_rowNumber: 4,
|
||||
_rawData: ['3', 'Tim', '34', '2020/04/28', 'FALSE'],
|
||||
id: '3',
|
||||
name: 'Tim',
|
||||
age: '34',
|
||||
birth_date: '2020/04/28',
|
||||
married: 'FALSE',
|
||||
_sheet: {},
|
||||
delete: mockDelete,
|
||||
},
|
||||
{
|
||||
_rowNumber: 5,
|
||||
_rawData: ['4', 'Craig', '21', '2020-04-25T22:00:00.000Z', 'TRUE'],
|
||||
id: '4',
|
||||
name: 'Craig',
|
||||
age: '120',
|
||||
birth_date: '2020-04-25T22:00:00.000Z',
|
||||
married: 'TRUE',
|
||||
_sheet: {},
|
||||
delete: mockDelete,
|
||||
},
|
||||
];
|
||||
return Promise.resolve(rows.slice(offset).slice(undefined, limit));
|
||||
};
|
||||
|
||||
const { resolver, schema, checkRead, checkWrite } = GoogleSheetDeleteMany;
|
||||
|
||||
beforeEach(() => {
|
||||
mockGetRows.mockReset();
|
||||
mockDelete.mockReset();
|
||||
});
|
||||
|
||||
test('googleSheetDeleteMany, match one', async () => {
|
||||
mockGetRows.mockImplementation(mockGetRowsDefaultImp);
|
||||
const res = await resolver({
|
||||
request: {
|
||||
filter: { id: '1' },
|
||||
},
|
||||
connection: {},
|
||||
});
|
||||
expect(res).toEqual({
|
||||
deletedCount: 1,
|
||||
});
|
||||
expect(mockDelete).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('googleSheetDeleteMany, match nothing', async () => {
|
||||
mockGetRows.mockImplementation(mockGetRowsDefaultImp);
|
||||
const res = await resolver({
|
||||
request: {
|
||||
filter: { id: 'does_not_exist' },
|
||||
},
|
||||
connection: {},
|
||||
});
|
||||
expect(res).toEqual({
|
||||
deletedCount: 0,
|
||||
});
|
||||
expect(mockDelete).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('googleSheetDeleteMany, match more than one', async () => {
|
||||
mockGetRows.mockImplementation(mockGetRowsDefaultImp);
|
||||
const res = await resolver({
|
||||
request: {
|
||||
filter: { _rowNumber: { $gt: 3 } },
|
||||
},
|
||||
connection: {},
|
||||
});
|
||||
expect(res).toEqual({
|
||||
deletedCount: 2,
|
||||
});
|
||||
expect(mockDelete).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test('valid request schema', () => {
|
||||
const request = {
|
||||
filter: { id: '1' },
|
||||
};
|
||||
expect(testSchema({ schema, object: request })).toBe(true);
|
||||
});
|
||||
|
||||
test('request properties is not an object', () => {
|
||||
const request = 'request';
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(ConfigurationError);
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(
|
||||
'GoogleSheetDeleteMany request properties should be an object.'
|
||||
);
|
||||
});
|
||||
|
||||
test('filter is not an object', () => {
|
||||
const request = {
|
||||
filter: true,
|
||||
};
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(ConfigurationError);
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(
|
||||
'GoogleSheetDeleteMany request property "filter" should be an object.'
|
||||
);
|
||||
});
|
||||
|
||||
test('filter is missing', () => {
|
||||
const request = {};
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(ConfigurationError);
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(
|
||||
'GoogleSheetDeleteMany request should have required property "filter".'
|
||||
);
|
||||
});
|
||||
|
||||
test('limit is not a number', () => {
|
||||
const request = {
|
||||
options: {
|
||||
limit: true,
|
||||
},
|
||||
};
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(ConfigurationError);
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(
|
||||
'GoogleSheetDeleteMany request property "options.limit" should be a number.'
|
||||
);
|
||||
});
|
||||
|
||||
test('skip is not a number', () => {
|
||||
const request = {
|
||||
options: {
|
||||
skip: true,
|
||||
},
|
||||
};
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(ConfigurationError);
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(
|
||||
'GoogleSheetDeleteMany request property "options.skip" should be a number.'
|
||||
);
|
||||
});
|
||||
|
||||
test('checkRead should be false', async () => {
|
||||
expect(checkRead).toBe(false);
|
||||
});
|
||||
|
||||
test('checkWrite should be true', async () => {
|
||||
expect(checkWrite).toBe(true);
|
||||
});
|
@ -0,0 +1,40 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Lowdefy Request Schema - GoogleSheetDeleteMany",
|
||||
"type": "object",
|
||||
"required": ["filter"],
|
||||
"properties": {
|
||||
"filter": {
|
||||
"type": "object",
|
||||
"description": "A MongoDB query expression to filter the data. All rows matched by the filter will be deleted",
|
||||
"errorMessage": {
|
||||
"type": "GoogleSheetDeleteMany request property \"filter\" should be an object."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "The maximum number of rows to fetch.",
|
||||
"errorMessage": {
|
||||
"type": "GoogleSheetDeleteMany request property \"options.limit\" should be a number."
|
||||
}
|
||||
},
|
||||
"skip": {
|
||||
"type": "number",
|
||||
"description": "The number of rows to skip from the top of the sheet.",
|
||||
"errorMessage": {
|
||||
"type": "GoogleSheetDeleteMany request property \"options.skip\" should be a number."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"errorMessage": {
|
||||
"type": "GoogleSheetDeleteMany request properties should be an object.",
|
||||
"required": {
|
||||
"filter": "GoogleSheetDeleteMany request should have required property \"filter\"."
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright 2020 Lowdefy, Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import schema from './GoogleSheetDeleteOneSchema.json';
|
||||
import cleanRows from '../cleanRows';
|
||||
import getSheet from '../getSheet';
|
||||
import mingoFilter from '../../../utils/mingoFilter';
|
||||
|
||||
async function googleSheetDeleteOne({ request, connection }) {
|
||||
const { filter, options = {} } = request;
|
||||
const { limit, skip } = options;
|
||||
const sheet = await getSheet({ connection });
|
||||
let rows = await sheet.getRows({ limit, offset: skip });
|
||||
rows = mingoFilter({ input: rows, filter });
|
||||
if (rows.length === 0) {
|
||||
return {
|
||||
deletedCount: 0,
|
||||
};
|
||||
}
|
||||
const row = rows[0];
|
||||
await row.delete();
|
||||
return {
|
||||
deletedCount: 1,
|
||||
row: cleanRows(row),
|
||||
};
|
||||
}
|
||||
|
||||
export default { resolver: googleSheetDeleteOne, schema, checkRead: false, checkWrite: true };
|
@ -0,0 +1,204 @@
|
||||
/*
|
||||
Copyright 2020 Lowdefy, Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import GoogleSheetDeleteOne from './GoogleSheetDeleteOne';
|
||||
import { ConfigurationError } from '../../../context/errors';
|
||||
import testSchema from '../../../utils/testSchema';
|
||||
|
||||
const mockGetRows = jest.fn();
|
||||
const mockDelete = jest.fn();
|
||||
jest.mock('../getSheet', () => () => ({
|
||||
getRows: mockGetRows,
|
||||
}));
|
||||
|
||||
const mockGetRowsDefaultImp = ({ limit, offset }) => {
|
||||
const rows = [
|
||||
{
|
||||
_rowNumber: 2,
|
||||
_rawData: ['1', 'John', '34', '2020/04/26', 'TRUE'],
|
||||
id: '1',
|
||||
name: 'John',
|
||||
age: '34',
|
||||
birth_date: '2020/04/26',
|
||||
married: 'TRUE',
|
||||
_sheet: {},
|
||||
delete: mockDelete,
|
||||
},
|
||||
{
|
||||
_rowNumber: 3,
|
||||
_rawData: ['2', 'Steven', '43', '2020/04/27', 'FALSE'],
|
||||
id: '2',
|
||||
name: 'Steve',
|
||||
age: '43',
|
||||
birth_date: '2020/04/27',
|
||||
married: 'FALSE',
|
||||
_sheet: {},
|
||||
delete: mockDelete,
|
||||
},
|
||||
{
|
||||
_rowNumber: 4,
|
||||
_rawData: ['3', 'Tim', '34', '2020/04/28', 'FALSE'],
|
||||
id: '3',
|
||||
name: 'Tim',
|
||||
age: '34',
|
||||
birth_date: '2020/04/28',
|
||||
married: 'FALSE',
|
||||
_sheet: {},
|
||||
delete: mockDelete,
|
||||
},
|
||||
{
|
||||
_rowNumber: 5,
|
||||
_rawData: ['4', 'Craig', '21', '2020-04-25T22:00:00.000Z', 'TRUE'],
|
||||
id: '4',
|
||||
name: 'Craig',
|
||||
age: '120',
|
||||
birth_date: '2020-04-25T22:00:00.000Z',
|
||||
married: 'TRUE',
|
||||
_sheet: {},
|
||||
delete: mockDelete,
|
||||
},
|
||||
];
|
||||
return Promise.resolve(rows.slice(offset).slice(undefined, limit));
|
||||
};
|
||||
|
||||
const { resolver, schema, checkRead, checkWrite } = GoogleSheetDeleteOne;
|
||||
|
||||
test('googleSheetDeleteMany, match one', async () => {
|
||||
mockGetRows.mockImplementation(mockGetRowsDefaultImp);
|
||||
const res = await resolver({
|
||||
request: {
|
||||
filter: { id: '1' },
|
||||
},
|
||||
connection: {},
|
||||
});
|
||||
expect(res).toEqual({
|
||||
deletedCount: 1,
|
||||
row: {
|
||||
_rowNumber: 2,
|
||||
_rawData: ['1', 'John', '34', '2020/04/26', 'TRUE'],
|
||||
id: '1',
|
||||
name: 'John',
|
||||
age: '34',
|
||||
birth_date: '2020/04/26',
|
||||
married: 'TRUE',
|
||||
delete: mockDelete,
|
||||
},
|
||||
});
|
||||
expect(mockDelete).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('googleSheetDeleteMany, match nothing', async () => {
|
||||
mockGetRows.mockImplementation(mockGetRowsDefaultImp);
|
||||
const res = await resolver({
|
||||
request: {
|
||||
filter: { id: 'does_not_exist' },
|
||||
},
|
||||
connection: {},
|
||||
});
|
||||
expect(res).toEqual({
|
||||
deletedCount: 0,
|
||||
});
|
||||
expect(mockDelete).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('googleSheetDeleteMany, match more than one', async () => {
|
||||
mockGetRows.mockImplementation(mockGetRowsDefaultImp);
|
||||
const res = await resolver({
|
||||
request: {
|
||||
filter: { _rowNumber: { $gt: 3 } },
|
||||
},
|
||||
connection: {},
|
||||
});
|
||||
expect(res).toEqual({
|
||||
deletedCount: 1,
|
||||
row: {
|
||||
_rawData: ['3', 'Tim', '34', '2020/04/28', 'FALSE'],
|
||||
_rowNumber: 4,
|
||||
age: '34',
|
||||
birth_date: '2020/04/28',
|
||||
delete: mockDelete,
|
||||
id: '3',
|
||||
married: 'FALSE',
|
||||
name: 'Tim',
|
||||
},
|
||||
});
|
||||
expect(mockDelete).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('valid request schema', () => {
|
||||
const request = {
|
||||
filter: { id: '1' },
|
||||
};
|
||||
expect(testSchema({ schema, object: request })).toBe(true);
|
||||
});
|
||||
|
||||
test('request properties is not an object', () => {
|
||||
const request = 'request';
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(ConfigurationError);
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(
|
||||
'GoogleSheetDeleteOne request properties should be an object.'
|
||||
);
|
||||
});
|
||||
|
||||
test('filter is not an object', () => {
|
||||
const request = {
|
||||
filter: true,
|
||||
};
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(ConfigurationError);
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(
|
||||
'GoogleSheetDeleteOne request property "filter" should be an object.'
|
||||
);
|
||||
});
|
||||
|
||||
test('filter is missing', () => {
|
||||
const request = {};
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(ConfigurationError);
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(
|
||||
'GoogleSheetDeleteOne request should have required property "filter".'
|
||||
);
|
||||
});
|
||||
|
||||
test('limit is not a number', () => {
|
||||
const request = {
|
||||
options: {
|
||||
limit: true,
|
||||
},
|
||||
};
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(ConfigurationError);
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(
|
||||
'GoogleSheetDeleteOne request property "options.limit" should be a number.'
|
||||
);
|
||||
});
|
||||
|
||||
test('skip is not a number', () => {
|
||||
const request = {
|
||||
options: {
|
||||
skip: true,
|
||||
},
|
||||
};
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(ConfigurationError);
|
||||
expect(() => testSchema({ schema, object: request })).toThrow(
|
||||
'GoogleSheetDeleteOne request property "options.skip" should be a number.'
|
||||
);
|
||||
});
|
||||
|
||||
test('checkRead should be false', async () => {
|
||||
expect(checkRead).toBe(false);
|
||||
});
|
||||
|
||||
test('checkWrite should be true', async () => {
|
||||
expect(checkWrite).toBe(true);
|
||||
});
|
@ -0,0 +1,40 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Lowdefy Request Schema - GoogleSheetDeleteOne",
|
||||
"type": "object",
|
||||
"required": ["filter"],
|
||||
"properties": {
|
||||
"filter": {
|
||||
"type": "object",
|
||||
"description": "A MongoDB query expression to filter the data. The first row matched by the filter will be deleted",
|
||||
"errorMessage": {
|
||||
"type": "GoogleSheetDeleteOne request property \"filter\" should be an object."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "The maximum number of rows to fetch.",
|
||||
"errorMessage": {
|
||||
"type": "GoogleSheetDeleteOne request property \"options.limit\" should be a number."
|
||||
}
|
||||
},
|
||||
"skip": {
|
||||
"type": "number",
|
||||
"description": "The number of rows to skip from the top of the sheet.",
|
||||
"errorMessage": {
|
||||
"type": "GoogleSheetDeleteOne request property \"options.skip\" should be a number."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"errorMessage": {
|
||||
"type": "GoogleSheetDeleteOne request properties should be an object.",
|
||||
"required": {
|
||||
"filter": "GoogleSheetDeleteOne request should have required property \"filter\"."
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user