Skip to content

Commit

Permalink
Merge pull request #43 from odroe/feat/$on
Browse files Browse the repository at this point in the history
feat: add `$on` method
  • Loading branch information
Seven Du authored Oct 8, 2022
2 parents 21285f8 + 68b6fe0 commit 62142bd
Show file tree
Hide file tree
Showing 14 changed files with 549 additions and 69 deletions.
33 changes: 33 additions & 0 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
## {next}

🌟 Help us spread the word about [Prisma ORM for Dart](https://github.com/odroe/prisma-dart) by starring the repo or [Tweeting](https://twitter.com/intent/tweet?text=Check%20out%20the%20latest%20@prisma%20ORM%20for%20Dart%20release%20v{next}🚀%0D%0A%0D%0Ahttps://github.com/odroe/prisma-dart/releases/tag/{next}) about the release. 🌟

### Features

#### Logging (Preview)

Prisma ORM for Dart now supports [logging](https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/logging) as a preview feature. That means, it might have some flaws, but we hope you'll try it out and provide feedback.

To enable logging, you need to set the `log` property on the `PrismaClient` constructor and `generate` command:

```bash
prisma generate --preview=logging
```

```dart
PrismaClient(
log: [
PrismaLogDefinition(
level: PrismaLogLevel.query,
emit: PrismaLogEvent.stdout,
),
],
)
```

##### Subscribe to log events

You can subscribe to log events to perform custom actions when a log occurs.

```dart
prisma.$on([PrismaLogLevel.query], (e) {
print(e);
});
```
120 changes: 98 additions & 22 deletions bin/src/generator/client_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ class ClientBuilder {
constructorBuilder.name = null; // Default constructor.
constructorBuilder.factory = true;

// Add constructor parameter.
// Add constructor datasources parameter.
constructorBuilder.optionalParameters
.add(Parameter((ParameterBuilder parameterBuilder) {
parameterBuilder.name = 'datasources';
Expand All @@ -212,6 +212,28 @@ class ClientBuilder {
});
}));

// Add constructor log parameter.
if (options.previewFeatures
.contains(GeneratorPreviewFeatures.logging)) {
constructorBuilder.optionalParameters
.add(Parameter((ParameterBuilder parameterBuilder) {
parameterBuilder.name = 'log';
parameterBuilder.named = true;
parameterBuilder.type =
TypeReference((TypeReferenceBuilder update) {
update.symbol = 'Iterable';
update.url = 'dart:core';
update.types.add(
refer('PrismaLogDefinition', 'package:orm/orm.dart'),
);
});

parameterBuilder.defaultTo = literalConstList(
[], refer('PrismaLogDefinition', 'package:orm/orm.dart'))
.code;
}));
}

// Create public constructor body.
constructorBuilder.body = Block((BlockBuilder blockBuilder) {
// Create a engine.
Expand Down Expand Up @@ -269,6 +291,50 @@ class ClientBuilder {
}));
}

// Create `$on` method.
// Output:
// void $on(
// Iterable<PrismaLogLevel> levels,
// PrismaLogHandler handler
// ) => _engine.logEmitter.on(levels.toSet(), handler);
if (options.previewFeatures.contains(GeneratorPreviewFeatures.logging)) {
classBuilder.methods.add(Method((MethodBuilder methodBuilder) {
methodBuilder.name = r'$on';
methodBuilder.requiredParameters.add(Parameter((ParameterBuilder b) {
b.name = 'levels';
b.type = TypeReference((TypeReferenceBuilder update) {
update.symbol = 'Iterable';
update.url = 'dart:core';
update.types.add(
refer('PrismaLogLevel', 'package:orm/orm.dart'),
);
});
}));
methodBuilder.requiredParameters.add(Parameter((ParameterBuilder b) {
b.name = 'handler';
b.type = refer('PrismaLogHandler', 'package:orm/orm.dart');
}));
methodBuilder.body =
refer('_engine').property('logEmitter').property('on').call([
refer('levels').property('toSet').call([]),
refer('handler'),
]).code;
methodBuilder.docs.add('''
/// The \$on() method allows you to subscribe to events.
///
/// All message use a [Exception] class wrapper.
///
/// Example:
/// ```dart
/// prisma.\$on([PrismaLogLevel.query], (e) {
/// if (e is PrismaQueryEvent) {
/// print(e.query);
/// }
/// });
/// ```''');
}));
}

// Create `$connect` method.
classBuilder.methods.add(Method((MethodBuilder methodBuilder) {
methodBuilder.name = r'$connect';
Expand Down Expand Up @@ -318,7 +384,7 @@ class ClientBuilder {
'///',
'/// Sometimes you need more control over what queries execute within a transaction. Interactive transactions are meant to provide you with an escape hatch.',
'///',
'/// **NOTE**: If you use interactive transactions, then you cannot use the [Data Proxy](https://www.prisma.io/docs/data-platform/data-proxy) at the same time.',
'/// **NOTE**: If you use interactive transactions, then you cannot use the [Data Proxy](https://prisma.pub/guides/preview-features#data-proxy) at the same time.',
'///',
'/// E.g:',
'/// ```dart',
Expand Down Expand Up @@ -431,30 +497,47 @@ class ClientBuilder {

/// Create engine instance.
Expression _createEngineInstance() {
if (options.dataProxy) {
return _createDataProxyEngineInstance();
}

return _createBinaryEngineInstance();
}

/// Create binary engine instance.
Expression _createBinaryEngineInstance() {
return refer('BinaryEngine', 'package:orm/orm.dart').newInstance([], {
// Create common named arguments.
final Map<String, Expression> namedArguments = {
'datasources': refer('datasources')
.nullSafeProperty('_toOverwrites')
.call([]).ifNullThen(literalMap({}, refer('String'),
refer('Datasource', 'package:orm/orm.dart'))),
'dmmf': refer('dmmf'),
'schema': refer('schema'),
'executable': refer('_executable'),
'environment':
refer('environment', 'package:orm/configure.dart').property('all'),
};

// Add logEmitter.
final logDefinitions =
options.previewFeatures.contains(GeneratorPreviewFeatures.logging)
? refer('log')
: literalConstList(
[], refer('PrismaLogDefinition', 'package:orm/orm.dart'));
namedArguments['logEmitter'] =
refer('PrismaLogEmitter', 'package:orm/orm.dart')
.newInstance([logDefinitions]);

if (options.dataProxy) {
return _createDataProxyEngineInstance(namedArguments);
}

return _createBinaryEngineInstance(namedArguments);
}

/// Create binary engine instance.
Expression _createBinaryEngineInstance(
Map<String, Expression> namedArguments) {
return refer('BinaryEngine', 'package:orm/orm.dart').newInstance([], {
...namedArguments,
'executable': refer('_executable'),
});
}

/// Create data proxy engine instance.
Expression _createDataProxyEngineInstance() {
Expression _createDataProxyEngineInstance(
Map<String, Expression> namedArguments) {
final Iterable<Expression> datasources =
options.datasources.map((DataSource datasource) {
return refer('Datasource', 'package:orm/orm.dart').newInstance([], {
Expand All @@ -465,14 +548,7 @@ class ClientBuilder {
});

return refer('DataProxyEngine', 'package:orm/orm.dart').newInstance([], {
'datasources': refer('datasources')
.nullSafeProperty('_toOverwrites')
.call([]).ifNullThen(literalMap({}, refer('String'),
refer('Datasource', 'package:orm/orm.dart'))),
'dmmf': refer('dmmf'),
'schema': refer('schema'),
'environment':
refer('environment', 'package:orm/configure.dart').property('all'),
...namedArguments,
'intenalDatasources': literalList(datasources),
});
}
Expand Down
4 changes: 3 additions & 1 deletion bin/src/generator/generator_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ enum GeneratorPreviewFeatures {
/// Prisma Data proxy
dataProxy(
'data-proxy', 'Enable `--data-proxy` flag for generated PrismaClient.'),
;

/// Prisma client `$on` method.
logging('logging', r'Enable `$on` method for generated PrismaClient.');

/// Create a new [GeneratorPreviewFeatures] instace.
const GeneratorPreviewFeatures(this.name, this.description);
Expand Down
37 changes: 37 additions & 0 deletions docs/guides/preview-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,40 @@ This feature is currently in preview state, you need to install `2.3.0` and abov
```bash
dart run orm generate --preview=finalizer
```

## Logging

Prisma ORM for Dart now supports [logging](https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/logging) as a preview feature. That means, it might have some flaws, but we hope you'll try it out and provide feedback.

To enable logging, you need to set the `log` property on the `PrismaClient` constructor and `generate` command:

```bash
dart run orm generate --preview=logging
```

```dart
PrismaClient(
log: [
PrismaLogDefinition(
level: PrismaLogLevel.query,
emit: PrismaLogEvent.stdout,
),
],
)
```

### Subscribe to log events

You can subscribe to log events to perform custom actions when a log occurs.

```dart
prisma.$on([PrismaLogLevel.query], (e) {
print(e);
});
```

### Difference

1. Prisma TS/JS client `log` has multi-type input, but Dart client only supports `PrismaLogDefinition` type because Dart does not support multi-type input.
2. Prisma TS/JS client `$on` can only subscribe to a single event, or all at once. But Dart clients can subscribe to multiple events.
3. Prisma TS/JS client log input is Object, Dart client try to satisfy [Event types](https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#event-types) Cases are wrapped with `Exception`.
13 changes: 9 additions & 4 deletions example/shelf_example/bin/src/prisma_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6253,7 +6253,8 @@ final _i3.Document dmmf = _i3.Document.fromJson(<String, dynamic>{
});
final String schema = _i4.utf8.decode(_i4.base64.decode(
r'Ly8gVGhpcyBpcyB5b3VyIFByaXNtYSBzY2hlbWEgZmlsZSwKLy8gbGVhcm4gbW9yZSBhYm91dCBpdCBpbiB0aGUgZG9jczogaHR0cHM6Ly9wcmlzLmx5L2QvcHJpc21hLXNjaGVtYQoKZ2VuZXJhdG9yIGNsaWVudCB7CiAgcHJvdmlkZXIgICAgICAgID0gInByaXNtYS1jbGllbnQtZGFydCIKICBwcmV2aWV3RmVhdHVyZXMgPSBbImludGVyYWN0aXZlVHJhbnNhY3Rpb25zIl0KICBvdXRwdXQgICAgICAgICAgPSAiLi4vYmluL3NyYy9wcmlzbWFfY2xpZW50LmRhcnQiCn0KCmRhdGFzb3VyY2UgZGIgewogIHByb3ZpZGVyID0gInBvc3RncmVzcWwiCiAgdXJsICAgICAgPSBlbnYoIkRBVEFCQVNFX1VSTCIpCn0KCm1vZGVsIFVzZXIgewogIGlkICAgICAgICBTdHJpbmcgICBAaWQgQGRlZmF1bHQoY3VpZCgpKQogIG5hbWUgICAgICBTdHJpbmcKICBjcmVhdGVkQXQgRGF0ZVRpbWUgQGRlZmF1bHQobm93KCkpCiAgdXBkYXRlZEF0IERhdGVUaW1lIEB1cGRhdGVkQXQKfQo='));
const String _executable = r'bin/q';
const String _executable =
r'/Users/seven/workspace/prisma/example/shelf_example/.dart_tool/prisma/query-engine';

class Datasources {
Datasources({this.db});
Expand Down Expand Up @@ -6282,13 +6283,15 @@ class PrismaClient {
datasources: datasources?._toOverwrites() ?? <String, _i2.Datasource>{},
dmmf: dmmf,
schema: schema,
executable: _executable,
environment: _i5.environment.all,
logEmitter: _i2.PrismaLogEmitter(const <_i2.PrismaLogDefinition>[]),
executable: _executable,
);
return PrismaClient._(
final PrismaClient client = PrismaClient._(
engine,
null,
);
return client;
}

final _i2.Engine _engine;
Expand All @@ -6304,7 +6307,9 @@ class PrismaClient {
Future<void> $connect() => _engine.start();

/// Disconnect from the database.
Future<void> $disconnect() => _engine.stop();
Future<void> $disconnect() async {
await _engine.stop();
}

/// Interactive transactions.
///
Expand Down
3 changes: 2 additions & 1 deletion example/shelf_example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ environment:
dependencies:
args: ^2.0.0
json_annotation: ^4.7.0
orm: ^2.2.3
orm:
path: ../../
shelf: ^1.1.0
shelf_router: ^1.0.0

Expand Down
5 changes: 3 additions & 2 deletions example/simple/bin/prisma_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16977,8 +16977,9 @@ class PrismaClient {
datasources: datasources?._toOverwrites() ?? <String, _i2.Datasource>{},
dmmf: dmmf,
schema: schema,
executable: _executable,
environment: _i5.environment.all,
logEmitter: _i2.PrismaLogEmitter(const <_i2.PrismaLogDefinition>[]),
executable: _executable,
);
final PrismaClient client = PrismaClient._(
engine,
Expand Down Expand Up @@ -17012,7 +17013,7 @@ class PrismaClient {
///
/// Sometimes you need more control over what queries execute within a transaction. Interactive transactions are meant to provide you with an escape hatch.
///
/// **NOTE**: If you use interactive transactions, then you cannot use the [Data Proxy](https://www.prisma.io/docs/data-platform/data-proxy) at the same time.
/// **NOTE**: If you use interactive transactions, then you cannot use the [Data Proxy](https://prisma.pub/guides/preview-features#data-proxy) at the same time.
///
/// E.g:
/// ```dart
Expand Down
1 change: 1 addition & 0 deletions lib/orm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export 'src/runtime/json_serializable.dart';
export 'src/runtime/language_keyword.dart';
export 'src/runtime/prisma_union.dart';
export 'src/runtime/prisma_null.dart';
export 'src/runtime/prisma_log.dart';

// GraphQL exports
export 'src/graphql/arg.dart';
Expand Down
Loading

1 comment on commit 62142bd

@vercel
Copy link

@vercel vercel bot commented on 62142bd Oct 8, 2022

Choose a reason for hiding this comment

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

Please sign in to comment.