first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled

This commit is contained in:
2026-03-17 16:22:57 +03:30
commit 3d5eaf9445
15349 changed files with 2847338 additions and 0 deletions
@@ -0,0 +1,623 @@
import type {
IDataObject,
IExecuteSingleFunctions,
IHttpRequestOptions,
INodeExecutionData,
INodeProperties,
} from 'n8n-workflow';
// Projection field controls the page limit maximum
// When not returning all, return the max number for the current projection parameter
const PAGE_LIMITS = {
noAcl: 1000,
full: 200,
};
// Define a JSON parse function here to use it in two places
async function parseJSONBody(
this: IExecuteSingleFunctions,
requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
if (!requestOptions.body) requestOptions.body = {};
const body = this.getNodeParameter('createBody') as IDataObject;
// Parse all the JSON fields
if (body.acl) {
try {
body.acl = JSON.parse(body.acl as string);
} catch (error) {}
}
if (body.billing) {
try {
body.billing = JSON.parse(body.billing as string);
} catch (error) {}
}
if (body.cors) {
try {
body.cors = JSON.parse(body.cors as string);
} catch (error) {}
}
if (body.customPlacementConfig) {
try {
body.customPlacementConfig = JSON.parse(body.customPlacementConfig as string);
} catch (error) {}
}
if (body.dataLocations) {
try {
body.dataLocations = JSON.parse(body.dataLocations as string);
} catch (error) {}
}
if (body.defaultObjectAcl) {
try {
body.defaultObjectAcl = JSON.parse(body.defaultObjectAcl as string);
} catch (error) {}
}
if (body.encryption) {
try {
body.encryption = JSON.parse(body.encryption as string);
} catch (error) {}
}
if (body.iamConfiguration) {
try {
body.iamConfiguration = JSON.parse(body.iamConfiguration as string);
} catch (error) {}
}
if (body.labels) {
try {
body.labels = JSON.parse(body.labels as string);
} catch (error) {}
}
if (body.lifecycle) {
try {
body.lifecycle = JSON.parse(body.lifecycle as string);
} catch (error) {}
}
if (body.logging) {
try {
body.logging = JSON.parse(body.logging as string);
} catch (error) {}
}
if (body.retentionPolicy) {
try {
body.retentionPolicy = JSON.parse(body.retentionPolicy as string);
} catch (error) {}
}
if (body.versioning) {
try {
body.versioning = JSON.parse(body.versioning as string);
} catch (error) {}
}
if (body.website) {
try {
body.website = JSON.parse(body.website as string);
} catch (error) {}
}
requestOptions.body = Object.assign(requestOptions.body, body);
return requestOptions;
}
export const bucketOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['bucket'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new Bucket',
routing: {
request: {
method: 'POST',
url: '/b/',
qs: {},
body: {
name: '={{$parameter["bucketName"]}}',
},
returnFullResponse: true,
},
send: {
preSend: [parseJSONBody],
},
},
action: 'Create a new Bucket',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an empty Bucket',
routing: {
request: {
method: 'DELETE',
url: '={{"/b/" + $parameter["bucketName"]}}',
returnFullResponse: true,
},
},
action: 'Delete an empty Bucket',
},
{
name: 'Get',
value: 'get',
description: 'Get metadata for a specific Bucket',
routing: {
request: {
method: 'GET',
url: '={{"/b/" + $parameter["bucketName"]}}',
returnFullResponse: true,
qs: {},
},
},
action: 'Get a Bucket',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get list of Buckets',
routing: {
request: {
method: 'GET',
url: '/b/',
qs: {},
},
send: {
paginate: true,
preSend: [
async function (this, requestOptions) {
if (!requestOptions.qs) requestOptions.qs = {};
const returnAll = this.getNodeParameter('returnAll') as boolean;
if (!returnAll) {
const key = this.getNodeParameter('projection') as string;
requestOptions.qs.maxResults =
key === 'noAcl' ? PAGE_LIMITS.noAcl : PAGE_LIMITS.full;
}
return requestOptions;
},
],
},
operations: {
async pagination(this, requestOptions) {
if (!requestOptions.options.qs) requestOptions.options.qs = {};
let executions: INodeExecutionData[] = [];
let responseData: INodeExecutionData[];
let nextPageToken: string | undefined = undefined;
const returnAll = this.getNodeParameter('returnAll') as boolean;
const extractBucketsList = (page: INodeExecutionData) => {
const buckets = page.json.items as IDataObject[];
if (buckets) {
executions = executions.concat(buckets.map((bucket) => ({ json: bucket })));
}
};
do {
requestOptions.options.qs.pageToken = nextPageToken;
responseData = await this.makeRoutingRequest(requestOptions);
// Check for another page
const lastItem = responseData[responseData.length - 1].json;
nextPageToken = lastItem.nextPageToken as string | undefined;
// Extract just the list of buckets from the page data
responseData.forEach(extractBucketsList);
// If we don't return all, just return the first page
} while (returnAll && nextPageToken);
// Return all execution responses as an array
return executions;
},
},
},
action: 'Get a list of Buckets for a given project',
},
{
name: 'Update',
value: 'update',
description: 'Update the metadata of a bucket',
routing: {
request: {
method: 'PATCH',
url: '={{"/b/" + $parameter["bucketName"]}}',
qs: {
project: '={{$parameter["projectId"]}}',
},
body: {},
returnFullResponse: true,
},
send: {
preSend: [parseJSONBody],
},
},
action: 'Update the metadata of a Bucket',
},
],
default: 'getAll',
},
];
export const bucketFields: INodeProperties[] = [
{
displayName: 'Project ID',
name: 'projectId',
type: 'string',
required: true,
placeholder: 'Project ID',
displayOptions: {
show: {
resource: ['bucket'],
operation: ['create', 'getAll'],
},
},
default: '',
routing: {
request: {
qs: {
project: '={{$value}}',
},
},
},
},
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
placeholder: 'Bucket Name',
required: true,
displayOptions: {
show: {
resource: ['bucket'],
operation: ['create', 'get', 'update', 'delete'],
},
},
default: '',
},
{
displayName: 'Prefix',
name: 'prefix',
type: 'string',
placeholder: 'Filter for Bucket Names',
displayOptions: {
show: {
resource: ['bucket'],
operation: ['getAll'],
},
},
default: '',
routing: {
request: {
qs: {
prefix: '={{$value}}',
},
},
},
},
{
displayName: 'Projection',
name: 'projection',
type: 'options',
noDataExpression: true,
options: [
{
name: 'All Properties',
value: 'full',
},
{
name: 'No ACL',
value: 'noAcl',
},
],
default: 'noAcl',
displayOptions: {
show: {
resource: ['bucket'],
operation: ['create', 'get', 'getAll', 'update'],
},
},
routing: {
request: {
qs: {
projection: '={{$value}}',
},
},
},
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['bucket'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Filters',
name: 'getFilters',
type: 'collection',
displayOptions: {
show: {
resource: ['bucket'],
operation: ['delete', 'get', 'update'],
},
},
default: {},
placeholder: 'Add Filter',
options: [
{
displayName: 'Metageneration Match',
name: 'ifMetagenerationMatch',
type: 'number',
description:
'Only return data if the metageneration value of the Bucket matches the sent value',
default: 0,
routing: {
request: {
qs: {
ifMetagenerationMatch: '={{$value}}',
},
},
},
},
{
displayName: 'Metageneration Exclude',
name: 'ifMetagenerationNotMatch',
type: 'number',
description:
'Only return data if the metageneration value of the Bucket does not match the sent value',
default: 0,
routing: {
request: {
qs: {
ifMetagenerationNotMatch: '={{$value}}',
},
},
},
},
],
},
{
displayName: 'Predefined Access Control',
name: 'createAcl',
type: 'collection',
noDataExpression: true,
default: {},
placeholder: 'Add Access Control Parameters',
displayOptions: {
show: {
resource: ['bucket'],
operation: ['create', 'update'],
},
},
options: [
{
displayName: 'Predefined ACL',
name: 'predefinedAcl',
type: 'options',
default: 'authenticatedRead',
placeholder: 'Apply a predefined set of access controls to this bucket',
options: [
{
name: 'Authenticated Read',
value: 'authenticatedRead',
},
{
name: 'Private',
value: 'private',
},
{
name: 'Project Private',
value: 'projectPrivate',
},
{
name: 'Public Read',
value: 'publicRead',
},
{
name: 'Public Read/Write',
value: 'publicReadWrite',
},
],
routing: {
request: {
qs: {
predefinedAcl: '={{$value}}',
},
},
},
},
{
displayName: 'Predefined Default Object ACL',
name: 'predefinedDefaultObjectAcl',
type: 'options',
default: 'authenticatedRead',
placeholder: 'Apply a predefined set of default object access controls to this bucket',
options: [
{
name: 'Authenticated Read',
value: 'authenticatedRead',
},
{
name: 'Bucket Owner Full Control',
value: 'bucketOwnerFullControl',
},
{
name: 'Bucket Owner Read',
value: 'bucketOwnerRead',
},
{
name: 'Private',
value: 'private',
},
{
name: 'Project Private',
value: 'projectPrivate',
},
{
name: 'Public Read',
value: 'publicRead',
},
],
routing: {
request: {
qs: {
predefinedObjectAcl: '={{$value}}',
},
},
},
},
],
},
{
displayName: 'Additional Parameters',
name: 'createBody',
type: 'collection',
noDataExpression: true,
default: {},
placeholder: 'Add Metadata Parameter',
displayOptions: {
show: {
resource: ['bucket'],
operation: ['create', 'update'],
},
},
options: [
{
displayName: 'Access Control',
name: 'acl',
type: 'json',
default: '[]',
placeholder: 'Access controls on the Bucket',
},
{
displayName: 'Billing',
name: 'billing',
type: 'json',
default: '{}',
placeholder: "The bucket's billing configuration",
},
{
displayName: 'CORS',
name: 'cors',
type: 'json',
default: '[]',
placeholder: "The bucket's Cross Origin Resource Sharing configuration",
},
{
displayName: 'Custom Placement Config',
name: 'customPlacementConfig',
type: 'json',
default: '{}',
placeholder: 'The configuration for the region(s) for the Bucket',
},
{
displayName: 'Data Locations',
name: 'dataLocations',
type: 'json',
default: '[]',
placeholder: 'The list of individual regions that comprise a dual-region Bucket',
},
{
displayName: 'Default Event Based Hold',
name: 'defaultEventBasedHold',
type: 'boolean',
default: true,
placeholder: 'Whether or not to automatically apply an event based hold to new objects',
},
{
displayName: 'Default Object ACL',
name: 'defaultObjectAcl',
type: 'json',
default: '[]',
placeholder: 'Default Access Controls for new objects when no ACL is provided',
},
{
displayName: 'Encryption',
name: 'encryption',
type: 'json',
default: '{}',
placeholder: 'Encryption configuration for a bucket',
},
{
displayName: 'IAM Configuration',
name: 'iamConfiguration',
type: 'json',
default: '{}',
placeholder: "The bucket's IAM configuration",
},
{
displayName: 'Labels',
name: 'labels',
type: 'json',
default: '{}',
placeholder: 'User provided bucket labels, in key/value pairs',
},
{
displayName: 'Lifecycle',
name: 'lifecycle',
type: 'json',
default: '{}',
placeholder: "The bucket's lifecycle configuration",
},
{
displayName: 'Location',
name: 'location',
type: 'string',
default: 'US',
placeholder: 'The location of the bucket',
},
{
displayName: 'Logging',
name: 'logging',
type: 'json',
default: '{}',
placeholder: "The bucket's logging configuration",
},
{
displayName: 'Retention Policy',
name: 'retentionPolicy',
type: 'json',
default: '{}',
placeholder: "The bucket's retention policy",
},
{
displayName: 'Recovery Point Objective',
name: 'rpo',
type: 'string',
default: 'DEFAULT',
placeholder: 'The recovery point objective for the bucket',
},
{
displayName: 'Storage Class',
name: 'storageClass',
type: 'string',
default: 'STANDARD',
placeholder: "The bucket's default storage class for objects that don't define one",
},
{
displayName: 'Versioning',
name: 'versioning',
type: 'json',
default: '{}',
placeholder: "The bucket's versioning configuration",
},
{
displayName: 'Website',
name: 'website',
type: 'json',
default: '{}',
placeholder: "The bucket's website configuration for when it is used to host a website",
},
],
},
];
@@ -0,0 +1,25 @@
{
"node": "n8n-nodes-base.googleCloudStorage",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Development", "Data & Storage"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/google/oauth-single-service/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlecloudstorage/"
}
],
"generic": [
{
"label": "15 Google apps you can combine and automate to increase productivity",
"icon": "💡",
"url": "https://n8n.io/blog/automate-google-apps-for-productivity/"
}
]
}
}
@@ -0,0 +1,66 @@
import { NodeConnectionTypes, type INodeType, type INodeTypeDescription } from 'n8n-workflow';
import { bucketFields, bucketOperations } from './BucketDescription';
import { objectFields, objectOperations } from './ObjectDescription';
export class GoogleCloudStorage implements INodeType {
description: INodeTypeDescription = {
displayName: 'Google Cloud Storage',
name: 'googleCloudStorage',
icon: 'file:googleCloudStorage.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Use the Google Cloud Storage API',
schemaPath: 'Google/CloudStorage',
defaults: {
name: 'Google Cloud Storage',
},
usableAsTool: true,
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
credentials: [
{
name: 'googleCloudStorageOAuth2Api',
required: true,
testedBy: {
request: {
method: 'GET',
url: '/b/',
},
},
},
],
requestDefaults: {
returnFullResponse: true,
baseURL: 'https://storage.googleapis.com/storage/v1',
},
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Bucket',
value: 'bucket',
},
{
name: 'Object',
value: 'object',
},
],
default: 'bucket',
},
// BUCKET
...bucketOperations,
...bucketFields,
// OBJECT
...objectOperations,
...objectFields,
],
};
}
@@ -0,0 +1,913 @@
import FormData from 'form-data';
import {
BINARY_ENCODING,
type IDataObject,
type INodeExecutionData,
type INodeProperties,
} from 'n8n-workflow';
import type { Readable } from 'stream';
// Define these because we'll be using them in two separate places
const metagenerationFilters: INodeProperties[] = [
{
displayName: 'Generation',
name: 'generation',
type: 'number',
placeholder: 'Select a specific revision of the chosen object',
default: -1,
},
{
displayName: 'Generation Match',
name: 'ifGenerationMatch',
type: 'number',
placeholder: 'Make operation conditional of the object generation matching this value',
default: -1,
},
{
displayName: 'Generation Exclude',
name: 'ifGenerationNotMatch',
type: 'number',
placeholder: 'Make operation conditional of the object generation not matching this value',
default: -1,
},
{
displayName: 'Metageneration Match',
name: 'ifMetagenerationMatch',
type: 'number',
placeholder:
"Make operation conditional of the object's current metageneration matching this value",
default: -1,
},
{
displayName: 'Metageneration Exclude',
name: 'ifMetagenerationNotMatch',
type: 'number',
placeholder:
"Make operation conditional of the object's current metageneration not matching this value",
default: -1,
},
];
const predefinedAclOptions: INodeProperties = {
displayName: 'Predefined ACL',
name: 'predefinedAcl',
type: 'options',
placeholder: 'Apply a predefined set of Access Controls to the object',
default: 'authenticatedRead',
options: [
{
name: 'Authenticated Read',
value: 'authenticatedRead',
},
{
name: 'Bucket Owner Full Control',
value: 'bucketOwnerFullControl',
},
{
name: 'Bucket Owner Read',
value: 'bucketOwnerRead',
},
{
name: 'Private',
value: 'private',
},
{
name: 'Project Private',
value: 'projectPrivate',
},
{
name: 'Public Read',
value: 'publicRead',
},
],
};
export const objectOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['object'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create an object',
routing: {
request: {
method: 'POST',
baseURL: 'https://storage.googleapis.com/upload/storage/v1',
url: '={{"/b/" + $parameter["bucketName"] + "/o/"}}',
qs: {
name: '={{$parameter["objectName"]}}',
uploadType: 'multipart',
},
headers: {},
},
send: {
preSend: [
// Handle setup of Query and Headers
async function (this, requestOptions) {
// Merge in the options into the queryset and headers objects
if (!requestOptions.qs) requestOptions.qs = {};
if (!requestOptions.headers) requestOptions.headers = {};
const options = this.getNodeParameter('createQuery') as IDataObject;
const headers = this.getNodeParameter('encryptionHeaders') as IDataObject;
requestOptions.qs = Object.assign(requestOptions.qs, options);
requestOptions.headers = Object.assign(requestOptions.headers, headers);
return requestOptions;
},
// Handle body creation
async function (this, requestOptions) {
// Populate metadata JSON
let metadata: IDataObject = { name: this.getNodeParameter('objectName') as string };
const bodyData = this.getNodeParameter('createData') as IDataObject;
const useBinary = this.getNodeParameter('createFromBinary') as boolean;
// Parse JSON body parameters
if (bodyData.acl) {
try {
bodyData.acl = JSON.parse(bodyData.acl as string);
} catch (error) {}
}
if (bodyData.metadata) {
try {
bodyData.metadata = JSON.parse(bodyData.metadata as string);
} catch (error) {}
}
metadata = Object.assign(metadata, bodyData);
// Populate request body
const body = new FormData();
body.append('metadata', JSON.stringify(metadata), {
contentType: 'application/json',
});
// Determine content and content type
let content: string | Buffer | Readable;
let contentType: string;
let contentLength: number;
if (useBinary) {
const binaryPropertyName = this.getNodeParameter(
'createBinaryPropertyName',
) as string;
const binaryData = this.helpers.assertBinaryData(binaryPropertyName);
if (binaryData.id) {
content = await this.helpers.getBinaryStream(binaryData.id);
const binaryMetadata = await this.helpers.getBinaryMetadata(binaryData.id);
contentType = binaryMetadata.mimeType ?? 'application/octet-stream';
contentLength = binaryMetadata.fileSize;
} else {
content = Buffer.from(binaryData.data, BINARY_ENCODING);
contentType = binaryData.mimeType;
contentLength = content.length;
}
} else {
content = this.getNodeParameter('createContent') as string;
contentType = 'text/plain';
contentLength = content.length;
}
body.append('file', content, { contentType, knownLength: contentLength });
// Set the headers
if (!requestOptions.headers) requestOptions.headers = {};
requestOptions.headers['Content-Length'] = body.getLengthSync();
requestOptions.headers['Content-Type'] =
`multipart/related; boundary=${body.getBoundary()}`;
// Return the request data
requestOptions.body = body;
return requestOptions;
},
],
},
},
action: 'Create an object',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete an object',
routing: {
request: {
method: 'DELETE',
url: '={{"/b/" + $parameter["bucketName"] + "/o/" + $parameter["objectName"]}}',
qs: {},
},
},
action: 'Delete an object from a bucket',
},
{
name: 'Get',
value: 'get',
description: 'Get object data or metadata',
routing: {
request: {
method: 'GET',
url: '={{"/b/" + $parameter["bucketName"] + "/o/" + $parameter["objectName"]}}',
returnFullResponse: true,
qs: {
alt: '={{$parameter["alt"]}}',
},
},
send: {
preSend: [
async function (this, requestOptions) {
if (!requestOptions.qs) requestOptions.qs = {};
if (!requestOptions.headers) requestOptions.headers = {};
const options = this.getNodeParameter('getParameters') as IDataObject;
const headers = this.getNodeParameter('encryptionHeaders') as IDataObject;
const datatype = this.getNodeParameter('alt') as string;
if (datatype === 'media') {
requestOptions.encoding = 'arraybuffer';
}
// Merge in the options into the queryset and headers objects
requestOptions.qs = Object.assign(requestOptions.qs, options);
requestOptions.headers = Object.assign(requestOptions.headers, headers);
// Return the request data
return requestOptions;
},
],
},
output: {
postReceive: [
async function (this, items, responseData) {
// If the request was for object data as opposed to metadata, change the json to binary field in the response
const datatype = this.getNodeParameter('alt') as string;
if (datatype === 'media') {
// Adapt the binaryProperty part of Routing Node since it's conditional
const destinationName = this.getNodeParameter('binaryPropertyName') as string;
const fileName = this.getNodeParameter('objectName') as string;
const binaryData = await this.helpers.prepareBinaryData(
responseData.body as Buffer,
fileName,
);
// Transform items
items = items.map((item) => {
item.json = {};
item.binary = { [destinationName]: binaryData };
return item;
});
}
return items;
},
],
},
},
action: 'Get object data or metadata',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Retrieve a list of objects',
routing: {
request: {
method: 'GET',
url: '={{"/b/" + $parameter["bucketName"] + "/o/"}}',
returnFullResponse: true,
qs: {},
},
send: {
preSend: [
async function (this, requestOptions) {
if (!requestOptions.qs) requestOptions.qs = {};
const options = this.getNodeParameter('listFilters') as IDataObject;
// Merge in the options into the queryset
requestOptions.qs = Object.assign(requestOptions.qs, options);
// Check if we send a limit
const returnAll = this.getNodeParameter('returnAll') as boolean;
if (!returnAll) requestOptions.qs.maxResults = this.getNodeParameter('maxResults');
// Return the request data
return requestOptions;
},
],
paginate: true,
},
operations: {
async pagination(this, requestOptions) {
if (!requestOptions.options.qs) requestOptions.options.qs = {};
let executions: INodeExecutionData[] = [];
let responseData: INodeExecutionData[];
let nextPageToken: string | undefined = undefined;
const returnAll = this.getNodeParameter('returnAll') as boolean;
const extractBucketsList = (page: INodeExecutionData) => {
const objects = page.json.items as IDataObject[];
if (objects) {
executions = executions.concat(objects.map((object) => ({ json: object })));
}
};
do {
requestOptions.options.qs.pageToken = nextPageToken;
responseData = await this.makeRoutingRequest(requestOptions);
// Check for another page
const lastItem = responseData[responseData.length - 1].json;
nextPageToken = lastItem.nextPageToken as string | undefined;
// Extract just the list of buckets from the page data
responseData.forEach(extractBucketsList);
} while (returnAll && nextPageToken);
// Return all execution responses as an array
return executions;
},
},
},
action: 'Get a list of objects',
},
{
name: 'Update',
value: 'update',
description: "Update an object's metadata",
routing: {
request: {
method: 'PATCH',
url: '={{"/b/" + $parameter["bucketName"] + "/o/" + $parameter["objectName"]}}',
qs: {},
body: {},
},
send: {
preSend: [
async function (this, requestOptions) {
if (!requestOptions.qs) requestOptions.qs = {};
if (!requestOptions.headers) requestOptions.headers = {};
if (!requestOptions.body) requestOptions.body = {};
const options = this.getNodeParameter('metagenAndAclQuery') as IDataObject;
const headers = this.getNodeParameter('encryptionHeaders') as IDataObject;
const body = this.getNodeParameter('updateData') as IDataObject;
// Parse JSON body parameters
if (body.acl) {
try {
body.acl = JSON.parse(body.acl as string);
} catch (error) {}
}
if (body.metadata) {
try {
body.metadata = JSON.parse(body.metadata as string);
} catch (error) {}
}
// Merge in the options into the queryset and headers objects
requestOptions.qs = Object.assign(requestOptions.qs, options);
requestOptions.headers = Object.assign(requestOptions.headers, headers);
requestOptions.body = Object.assign(requestOptions.body, body);
// Return the request data
return requestOptions;
},
],
},
},
action: "Update an object's metadata",
},
],
default: 'getAll',
},
];
export const objectFields: INodeProperties[] = [
{
displayName: 'Bucket Name',
name: 'bucketName',
type: 'string',
placeholder: 'Bucket Name',
required: true,
displayOptions: {
show: {
resource: ['object'],
},
},
default: '',
},
{
displayName: 'Object Name',
name: 'objectName',
type: 'string',
placeholder: 'Object Name',
required: true,
displayOptions: {
show: {
resource: ['object'],
operation: ['create', 'delete', 'get', 'update'],
},
},
default: '',
},
{
displayName: 'Projection',
name: 'projection',
type: 'options',
noDataExpression: true,
options: [
{
name: 'All Properties',
value: 'full',
},
{
name: 'No ACL',
value: 'noAcl',
},
],
default: 'noAcl',
displayOptions: {
show: {
resource: ['object'],
operation: ['get', 'getAll'],
},
},
routing: {
request: {
qs: {
projection: '={{$value}}',
},
},
},
},
// Create / Update gets their own definition because the default value is swapped
{
displayName: 'Projection',
name: 'updateProjection',
type: 'options',
noDataExpression: true,
options: [
{
name: 'All Properties',
value: 'full',
},
{
name: 'No ACL',
value: 'noAcl',
},
],
default: 'full',
displayOptions: {
show: {
resource: ['object'],
operation: ['create', 'update'],
},
},
routing: {
request: {
qs: {
projection: '={{$value}}',
},
},
},
},
{
displayName: 'Return Data',
name: 'alt',
type: 'options',
placeholder: 'The type of data to return from the request',
default: 'json',
options: [
{
name: 'Metadata',
value: 'json',
},
{
name: 'Object Data',
value: 'media',
},
],
displayOptions: {
show: {
resource: ['object'],
operation: ['get'],
},
},
},
{
displayName: 'Use Input Binary Field',
name: 'createFromBinary',
type: 'boolean',
displayOptions: {
show: {
resource: ['object'],
operation: ['create'],
},
},
default: true,
noDataExpression: true,
description: 'Whether the data for creating a file should come from a binary field',
},
{
displayName: 'Input Binary Field',
name: 'createBinaryPropertyName',
type: 'string',
hint: 'The name of the input binary field containing the file to be written',
displayOptions: {
show: {
resource: ['object'],
operation: ['create'],
createFromBinary: [true],
},
},
default: 'data',
},
{
displayName: 'File Content',
name: 'createContent',
type: 'string',
displayOptions: {
show: {
resource: ['object'],
operation: ['create'],
createFromBinary: [false],
},
},
default: '',
description: 'Content of the file to be uploaded',
},
{
displayName: 'Put Output File in Field',
name: 'binaryPropertyName',
type: 'string',
hint: 'The name of the output binary field to put the file in',
displayOptions: {
show: {
resource: ['object'],
operation: ['get'],
alt: ['media'],
},
},
default: 'data',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['object'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'maxResults',
type: 'number',
displayOptions: {
show: {
resource: ['object'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 1000,
},
default: 100,
description: 'Max number of results to return',
},
{
displayName: 'Create Fields',
name: 'createData',
type: 'collection',
placeholder: 'Add Create Body Field',
displayOptions: {
show: {
resource: ['object'],
operation: ['create'],
},
},
default: {},
options: [
{
displayName: 'Access Control List',
name: 'acl',
type: 'json',
default: '[]',
},
{
displayName: 'Cache Control',
name: 'cacheControl',
type: 'string',
default: '',
},
{
displayName: 'Content Disposition',
name: 'contentDisposition',
type: 'string',
default: '',
},
{
displayName: 'Content Encoding',
name: 'contentEncoding',
type: 'string',
default: '',
},
{
displayName: 'Content Language',
name: 'contentLanguage',
type: 'string',
default: '',
},
{
displayName: 'Content Type',
name: 'contentType',
type: 'string',
default: '',
},
{
displayName: 'CRC32c Checksum',
name: 'crc32c',
type: 'string',
default: '',
},
{
displayName: 'Custom Time',
name: 'customTime',
type: 'string',
default: '',
},
{
displayName: 'Event Based Hold',
name: 'eventBasedHold',
type: 'boolean',
default: false,
},
{
displayName: 'MD5 Hash',
name: 'md5Hash',
type: 'string',
default: '',
},
{
displayName: 'Metadata',
name: 'metadata',
type: 'json',
default: '{}',
},
{
displayName: 'Storage Class',
name: 'storageClass',
type: 'string',
default: '',
},
{
displayName: 'Temporary Hold',
name: 'temporaryHold',
type: 'boolean',
default: false,
},
],
},
{
displayName: 'Update Fields',
name: 'updateData',
type: 'collection',
placeholder: 'Add Update Body Field',
displayOptions: {
show: {
resource: ['object'],
operation: ['update'],
},
},
default: {
acl: '[]',
},
options: [
{
displayName: 'Access Control',
name: 'acl',
type: 'json',
default: '[]',
},
{
displayName: 'Cache Control',
name: 'cacheControl',
type: 'string',
default: '',
},
{
displayName: 'Content Disposition',
name: 'contentDisposition',
type: 'string',
default: '',
},
{
displayName: 'Content Encoding',
name: 'contentEncoding',
type: 'string',
default: '',
},
{
displayName: 'Content Language',
name: 'contentLanguage',
type: 'string',
default: '',
},
{
displayName: 'Content Type',
name: 'contentType',
type: 'string',
default: '',
},
{
displayName: 'Custom Time',
name: 'customTime',
type: 'string',
default: '',
},
{
displayName: 'Event Based Hold',
name: 'eventBasedHold',
type: 'boolean',
default: false,
},
{
displayName: 'Metadata',
name: 'metadata',
type: 'json',
default: '{}',
},
{
displayName: 'Temporary Hold',
name: 'temporaryHold',
type: 'boolean',
default: false,
},
],
},
{
displayName: 'Additional Parameters',
name: 'createQuery',
type: 'collection',
placeholder: 'Add Additional Parameters',
displayOptions: {
show: {
resource: ['object'],
operation: ['create'],
},
},
default: {},
options: [
{
displayName: 'Content Encoding',
name: 'contentEncoding',
type: 'string',
default: '',
},
...metagenerationFilters,
{
displayName: 'KMS Key Name',
name: 'kmsKeyName',
type: 'string',
default: '',
},
predefinedAclOptions,
],
},
{
displayName: 'Additional Parameters',
name: 'getParameters',
type: 'collection',
placeholder: 'Add Additional Parameters',
displayOptions: {
show: {
resource: ['object'],
operation: ['delete', 'get'],
},
},
default: {},
options: [...metagenerationFilters],
},
{
displayName: 'Additional Parameters',
name: 'metagenAndAclQuery',
type: 'collection',
placeholder: 'Add Additional Parameters',
displayOptions: {
show: {
resource: ['object'],
operation: ['update'],
},
},
default: {},
options: [...metagenerationFilters, predefinedAclOptions],
},
{
displayName: 'Encryption Headers',
name: 'encryptionHeaders',
type: 'collection',
placeholder: 'Add Encryption Headers',
displayOptions: {
show: {
resource: ['object'],
operation: ['create', 'get', 'update'],
},
},
default: {},
options: [
{
displayName: 'Encryption Algorithm',
name: 'X-Goog-Encryption-Algorithm',
type: 'options',
placeholder:
'The encryption algorithm to use, which must be AES256. Use to supply your own key in the request',
default: 'AES256',
options: [
{
name: 'AES256',
value: 'AES256',
},
],
},
{
displayName: 'Encryption Key',
name: 'X-Goog-Encryption-Key',
type: 'string',
placeholder: 'Base64 encoded string of your AES256 encryption key',
default: '',
},
{
displayName: 'Encryption Key Hash',
name: 'X-Goog-Encryption-Key-Sha256',
type: 'string',
placeholder: 'Base64 encoded string of the SHA256 hash of your encryption key',
default: '',
},
],
},
{
displayName: 'Additional Parameters',
name: 'listFilters',
type: 'collection',
placeholder: 'Add Additional Parameters',
displayOptions: {
show: {
resource: ['object'],
operation: ['getAll'],
},
},
default: {},
options: [
{
displayName: 'Delimiter',
name: 'delimiter',
type: 'string',
placeholder: 'Returns results in directory-like mode, using this value as the delimiter',
default: '/',
},
{
displayName: 'End Offset',
name: 'endOffset',
type: 'string',
placeholder: 'Filter results to names lexicographically before this value',
default: '',
},
{
displayName: 'Include Trailing Delimiter',
name: 'includeTrailingDelimiter',
type: 'boolean',
placeholder:
'If true, objects will appear with exactly one instance of delimiter at the end of the name',
default: false,
},
{
displayName: 'Prefix',
name: 'prefix',
type: 'string',
placeholder: 'Filter results to names that start with this value',
default: '',
},
{
displayName: 'Start Offset',
name: 'startOffset',
type: 'string',
placeholder: 'Filter results to names lexicographically equal or after this value',
default: '',
},
{
displayName: 'Versions',
name: 'versions',
type: 'boolean',
placeholder: 'If true, list all versions of objects as distinct entries',
default: false,
},
],
},
];
@@ -0,0 +1,83 @@
{
"type": "object",
"properties": {
"etag": {
"type": "string"
},
"generation": {
"type": "string"
},
"iamConfiguration": {
"type": "object",
"properties": {
"bucketPolicyOnly": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
}
}
},
"publicAccessPrevention": {
"type": "string"
},
"uniformBucketLevelAccess": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
}
}
}
}
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"location": {
"type": "string"
},
"locationType": {
"type": "string"
},
"metageneration": {
"type": "string"
},
"name": {
"type": "string"
},
"projectNumber": {
"type": "string"
},
"rpo": {
"type": "string"
},
"selfLink": {
"type": "string"
},
"softDeletePolicy": {
"type": "object",
"properties": {
"effectiveTime": {
"type": "string"
},
"retentionDurationSeconds": {
"type": "string"
}
}
},
"storageClass": {
"type": "string"
},
"timeCreated": {
"type": "string"
},
"updated": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,94 @@
{
"type": "object",
"properties": {
"etag": {
"type": "string"
},
"generation": {
"type": "string"
},
"hierarchicalNamespace": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
}
}
},
"iamConfiguration": {
"type": "object",
"properties": {
"bucketPolicyOnly": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"lockedTime": {
"type": "string"
}
}
},
"publicAccessPrevention": {
"type": "string"
},
"uniformBucketLevelAccess": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"lockedTime": {
"type": "string"
}
}
}
}
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"location": {
"type": "string"
},
"locationType": {
"type": "string"
},
"metageneration": {
"type": "string"
},
"name": {
"type": "string"
},
"projectNumber": {
"type": "string"
},
"selfLink": {
"type": "string"
},
"softDeletePolicy": {
"type": "object",
"properties": {
"effectiveTime": {
"type": "string"
},
"retentionDurationSeconds": {
"type": "string"
}
}
},
"storageClass": {
"type": "string"
},
"timeCreated": {
"type": "string"
},
"updated": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,97 @@
{
"type": "object",
"properties": {
"etag": {
"type": "string"
},
"generation": {
"type": "string"
},
"hierarchicalNamespace": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
}
}
},
"iamConfiguration": {
"type": "object",
"properties": {
"bucketPolicyOnly": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"lockedTime": {
"type": "string"
}
}
},
"publicAccessPrevention": {
"type": "string"
},
"uniformBucketLevelAccess": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"lockedTime": {
"type": "string"
}
}
}
}
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"location": {
"type": "string"
},
"locationType": {
"type": "string"
},
"metageneration": {
"type": "string"
},
"name": {
"type": "string"
},
"projectNumber": {
"type": "string"
},
"rpo": {
"type": "string"
},
"selfLink": {
"type": "string"
},
"softDeletePolicy": {
"type": "object",
"properties": {
"effectiveTime": {
"type": "string"
},
"retentionDurationSeconds": {
"type": "string"
}
}
},
"storageClass": {
"type": "string"
},
"timeCreated": {
"type": "string"
},
"updated": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,97 @@
{
"type": "object",
"properties": {
"etag": {
"type": "string"
},
"generation": {
"type": "string"
},
"hierarchicalNamespace": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
}
}
},
"iamConfiguration": {
"type": "object",
"properties": {
"bucketPolicyOnly": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"lockedTime": {
"type": "string"
}
}
},
"publicAccessPrevention": {
"type": "string"
},
"uniformBucketLevelAccess": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"lockedTime": {
"type": "string"
}
}
}
}
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"location": {
"type": "string"
},
"locationType": {
"type": "string"
},
"metageneration": {
"type": "string"
},
"name": {
"type": "string"
},
"projectNumber": {
"type": "string"
},
"rpo": {
"type": "string"
},
"selfLink": {
"type": "string"
},
"softDeletePolicy": {
"type": "object",
"properties": {
"effectiveTime": {
"type": "string"
},
"retentionDurationSeconds": {
"type": "string"
}
}
},
"storageClass": {
"type": "string"
},
"timeCreated": {
"type": "string"
},
"updated": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,60 @@
{
"type": "object",
"properties": {
"bucket": {
"type": "string"
},
"contentType": {
"type": "string"
},
"crc32c": {
"type": "string"
},
"etag": {
"type": "string"
},
"generation": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"md5Hash": {
"type": "string"
},
"mediaLink": {
"type": "string"
},
"metageneration": {
"type": "string"
},
"name": {
"type": "string"
},
"selfLink": {
"type": "string"
},
"size": {
"type": "string"
},
"storageClass": {
"type": "string"
},
"timeCreated": {
"type": "string"
},
"timeFinalized": {
"type": "string"
},
"timeStorageClassUpdated": {
"type": "string"
},
"updated": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,9 @@
{
"type": "object",
"properties": {
"error": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,60 @@
{
"type": "object",
"properties": {
"bucket": {
"type": "string"
},
"contentType": {
"type": "string"
},
"crc32c": {
"type": "string"
},
"etag": {
"type": "string"
},
"generation": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"md5Hash": {
"type": "string"
},
"mediaLink": {
"type": "string"
},
"metageneration": {
"type": "string"
},
"name": {
"type": "string"
},
"selfLink": {
"type": "string"
},
"size": {
"type": "string"
},
"storageClass": {
"type": "string"
},
"timeCreated": {
"type": "string"
},
"timeFinalized": {
"type": "string"
},
"timeStorageClassUpdated": {
"type": "string"
},
"updated": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1,66 @@
{
"type": "object",
"properties": {
"bucket": {
"type": "string"
},
"contentType": {
"type": "string"
},
"crc32c": {
"type": "string"
},
"etag": {
"type": "string"
},
"eventBasedHold": {
"type": "boolean"
},
"generation": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"md5Hash": {
"type": "string"
},
"mediaLink": {
"type": "string"
},
"metageneration": {
"type": "string"
},
"name": {
"type": "string"
},
"selfLink": {
"type": "string"
},
"size": {
"type": "string"
},
"storageClass": {
"type": "string"
},
"temporaryHold": {
"type": "boolean"
},
"timeCreated": {
"type": "string"
},
"timeFinalized": {
"type": "string"
},
"timeStorageClassUpdated": {
"type": "string"
},
"updated": {
"type": "string"
}
},
"version": 4
}
@@ -0,0 +1,60 @@
{
"type": "object",
"properties": {
"bucket": {
"type": "string"
},
"contentType": {
"type": "string"
},
"crc32c": {
"type": "string"
},
"etag": {
"type": "string"
},
"generation": {
"type": "string"
},
"id": {
"type": "string"
},
"kind": {
"type": "string"
},
"md5Hash": {
"type": "string"
},
"mediaLink": {
"type": "string"
},
"metageneration": {
"type": "string"
},
"name": {
"type": "string"
},
"selfLink": {
"type": "string"
},
"size": {
"type": "string"
},
"storageClass": {
"type": "string"
},
"timeCreated": {
"type": "string"
},
"timeFinalized": {
"type": "string"
},
"timeStorageClassUpdated": {
"type": "string"
},
"updated": {
"type": "string"
}
},
"version": 1
}
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><defs><style>.cls-1{fill:#aecbfa}.cls-2{fill:#669df6}.cls-3{fill:#4285f4}.cls-4{fill:#fff}</style></defs><g data-name="Product Icons"><path d="M2 4h20v7H2z" class="cls-1"/><path d="M20 4h2v7h-2z" class="cls-2"/><path d="M22 4h-2v7z" class="cls-3"/><path d="M2 4h2v7H2z" class="cls-2"/><path d="M6 7h6v1H6z" class="cls-4"/><rect width="3" height="3" x="15" y="6" class="cls-4" rx="1.5"/><path d="M2 13h20v7H2z" class="cls-1"/><path d="M20 13h2v7h-2z" class="cls-2"/><path d="M22 13h-2v7z" class="cls-3"/><path d="M2 13h2v7H2z" class="cls-2"/><path d="M6 16h6v1H6z" class="cls-4"/><rect width="3" height="3" x="15" y="15" class="cls-4" rx="1.5"/></g></svg>

After

Width:  |  Height:  |  Size: 717 B