Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkgs/gcloud/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.10.0

- Add support for Cloud Tasks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to also update the README.md


## 0.9.0

- Support `orderingKey` on pub/sub's `Message` type.
Expand Down
68 changes: 68 additions & 0 deletions pkgs/gcloud/lib/cloud_tasks.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/// This library provides a low-level API for accessing Google's Cloud
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the std 3-line copyright header here? For 2025.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we call the file tasks.dart instead of cloud_tasks.dart (to align with storage.dart etc.)

/// Tasks.
///
/// For more information on Cloud Tasks, please refer to the following
/// developers page: https://cloud.google.com/tasks/docs
library;

import 'package:googleapis/cloudtasks/v2.dart' as tasks;
import 'package:http/http.dart' as http;

import 'service_scope.dart' as ss;

import 'src/cloud_tasks_impl.dart' show CloudTasksImpl;

const Symbol _tasksKey = #gcloud.tasks;

/// Access the [CloudTasks] object available in the current service scope.
///
/// The returned object will be the one which was previously registered with
/// [registerTasksService] within the current (or a parent) service scope.
///
/// Accessing this getter outside of a service scope will result in an error.
/// See the `package:gcloud/service_scope.dart` library for more information.
CloudTasks get tasksService => ss.lookup(_tasksKey) as CloudTasks;

/// Registers the [CloudTasks] object within the current service scope.
///
/// The provided `tasks` object will be available via the top-level
/// `tasksService` getter.
///
/// Calling this function outside of a service scope will result in an error.
/// Calling this function more than once inside the same service scope is not
/// allowed.
void registerTasksService(CloudTasks tasks) {
ss.register(_tasksKey, tasks);
}

/// Interface used to talk to the Google Cloud Tasks service.
abstract class CloudTasks {
/// List of required OAuth2 scopes for Cloud Tasks operations.
// ignore: constant_identifier_names
static const Scopes = [tasks.CloudTasksApi.cloudPlatformScope];

/// Access Cloud Tasks using an authenticated client.
///
/// The [client] is an authenticated HTTP client. This client must
/// provide access to at least the scopes in [CloudTasks.Scopes].
factory CloudTasks(http.Client client, String project, String location) {
return CloudTasksImpl(client, project, location);
}

/// Creates a new task on the specified [queue]. When the task is run,
/// [request] will be executed.
///
/// If [scheduleTime] is provided, the task will be scheduled to run at the
/// specified time. If not provided, the task will be scheduled to run
/// immediately.
///
/// If [name] is provided, the task will be given that name.
///
/// Returns a [Future] which completes with the newly created task.
Future<tasks.Task> createTask(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should wrap the Task type in a more opinionated wrapper, such that we don't expose the types from package:googleapis.

Like we do here

class _BucketInfoImpl implements BucketInfo {

String queue,
http.Request request, {
String? name,
DateTime? scheduleTime,
});
}
47 changes: 47 additions & 0 deletions pkgs/gcloud/lib/src/cloud_tasks_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'dart:convert';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3-line copyright header


import 'package:googleapis/cloudtasks/v2.dart' as api;
import 'package:http/http.dart' as http;

import '../cloud_tasks.dart' as tasks;

class CloudTasksImpl implements tasks.CloudTasks {
final api.CloudTasksApi _api;
final String _project;
final String _location;

CloudTasksImpl(http.Client client, String project, String location)
: _api = api.CloudTasksApi(client),
_project = project,
_location = location;

@override
Future<api.Task> createTask(
String queue,
http.Request request, {
DateTime? scheduleTime,
String? name,
}) async {
final task = api.Task(
name: name,
scheduleTime: scheduleTime?.toUtc().toIso8601String(),
httpRequest: _fromHttpRequest(request),
);
final createTaskRequest = api.CreateTaskRequest(task: task);
return _api.projects.locations.queues.tasks
.create(createTaskRequest, _fullQueueName(queue));
}

api.HttpRequest _fromHttpRequest(http.Request request) {
return api.HttpRequest(
headers: request.headers,
body: base64.encode(utf8.encode(request.body)),
httpMethod: request.method,
url: request.url.toString(),
);
}

String _fullQueueName(String name) {
return 'projects/$_project/locations/$_location/queues/$name';
}
}
2 changes: 1 addition & 1 deletion pkgs/gcloud/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: gcloud
version: 0.9.0
version: 0.10.0
description: >-
High level idiomatic Dart API for Google Cloud Storage, Pub-Sub and Datastore.
repository: https://github.com/dart-lang/labs/tree/main/pkgs/gcloud
Expand Down
65 changes: 65 additions & 0 deletions pkgs/gcloud/test/cloud_tasks/cloud_tasks_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import 'dart:convert';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copyright header


import 'package:gcloud/cloud_tasks.dart';
import 'package:http/http.dart' as http;
import 'package:test/test.dart';

import '../common.dart';
import '../common_e2e.dart';

const _hostName = 'cloudtasks.googleapis.com';
const _rootPath = '/v2/';
const _defaultLocation = 'us-central1';

MockClient mockClient() => MockClient(_hostName, _rootPath);

Future<void> withMockClientAsync(
Future Function(MockClient client, CloudTasks tasks) function) async {
var mock = mockClient();
await function(mock, CloudTasks(mock, testProject, _defaultLocation));
}

void main() {
group('task', () {
test('create with httpRequest', () async {
const taskName = 'my-task';
const requestBody = 'hello world';
const requestHeaders = {
'content-type': 'application/json',
'authorization': 'Bearer ....'
};
final expectedTaskJson = {
'name': taskName,
'httpRequest': {
'headers': requestHeaders,
'body': base64.encode(utf8.encode(requestBody)),
'httpMethod': 'GET',
'url': 'https://www.google.com',
},
};
final responseJson = {'task': expectedTaskJson};

await withMockClientAsync((mock, tasks) async {
mock.register(
'POST',
'projects/test-project/locations/us-central1/queues/test-queue/tasks',
expectAsync1((request) {
final json = jsonDecode(request.body) as Map<String, dynamic>;
expect(json, responseJson);
return mock.respond(expectedTaskJson);
}),
);

final request = http.Request('GET', Uri.parse('https://www.google.com'))
..body = requestBody
..headers.addAll(requestHeaders);

final task =
await tasks.createTask('test-queue', request, name: taskName);
expect(task.name, taskName);
final actualRequestJson = task.httpRequest!.toJson();
expect(actualRequestJson, expectedTaskJson['httpRequest']);
});
});
});
}