define quote endpoint

This commit is contained in:
tk 2025-08-16 14:15:21 +02:00
parent c952f17e30
commit ff8de67cbb
10 changed files with 614 additions and 132 deletions

View file

@ -13,13 +13,12 @@ import 'package:serverpod_client/serverpod_client.dart' as _i1;
abstract class Quote implements _i1.SerializableModel {
Quote._({
required this.id,
this.id,
required this.userId,
required this.text,
this.authorName,
required this.lat,
required this.lng,
required this.geohash,
required this.long,
required this.createdAt,
required this.visibility,
required this.upvotes,
@ -28,13 +27,12 @@ abstract class Quote implements _i1.SerializableModel {
});
factory Quote({
required int id,
int? id,
required int userId,
required String text,
String? authorName,
required double lat,
required double lng,
required String geohash,
required double long,
required DateTime createdAt,
required int visibility,
required int upvotes,
@ -44,13 +42,12 @@ abstract class Quote implements _i1.SerializableModel {
factory Quote.fromJson(Map<String, dynamic> jsonSerialization) {
return Quote(
id: jsonSerialization['id'] as int,
id: jsonSerialization['id'] as int?,
userId: jsonSerialization['userId'] as int,
text: jsonSerialization['text'] as String,
authorName: jsonSerialization['authorName'] as String?,
lat: (jsonSerialization['lat'] as num).toDouble(),
lng: (jsonSerialization['lng'] as num).toDouble(),
geohash: jsonSerialization['geohash'] as String,
long: (jsonSerialization['long'] as num).toDouble(),
createdAt:
_i1.DateTimeJsonExtension.fromJson(jsonSerialization['createdAt']),
visibility: jsonSerialization['visibility'] as int,
@ -62,7 +59,10 @@ abstract class Quote implements _i1.SerializableModel {
);
}
int id;
/// The database id, set if the object has been inserted into the
/// database or if it has been fetched from the database. Otherwise,
/// the id will be null.
int? id;
int userId;
@ -72,9 +72,7 @@ abstract class Quote implements _i1.SerializableModel {
double lat;
double lng;
String geohash;
double long;
DateTime createdAt;
@ -95,8 +93,7 @@ abstract class Quote implements _i1.SerializableModel {
String? text,
String? authorName,
double? lat,
double? lng,
String? geohash,
double? long,
DateTime? createdAt,
int? visibility,
int? upvotes,
@ -106,13 +103,12 @@ abstract class Quote implements _i1.SerializableModel {
@override
Map<String, dynamic> toJson() {
return {
'id': id,
if (id != null) 'id': id,
'userId': userId,
'text': text,
if (authorName != null) 'authorName': authorName,
'lat': lat,
'lng': lng,
'geohash': geohash,
'long': long,
'createdAt': createdAt.toJson(),
'visibility': visibility,
'upvotes': upvotes,
@ -131,13 +127,12 @@ class _Undefined {}
class _QuoteImpl extends Quote {
_QuoteImpl({
required int id,
int? id,
required int userId,
required String text,
String? authorName,
required double lat,
required double lng,
required String geohash,
required double long,
required DateTime createdAt,
required int visibility,
required int upvotes,
@ -149,8 +144,7 @@ class _QuoteImpl extends Quote {
text: text,
authorName: authorName,
lat: lat,
lng: lng,
geohash: geohash,
long: long,
createdAt: createdAt,
visibility: visibility,
upvotes: upvotes,
@ -163,13 +157,12 @@ class _QuoteImpl extends Quote {
@_i1.useResult
@override
Quote copyWith({
int? id,
Object? id = _Undefined,
int? userId,
String? text,
Object? authorName = _Undefined,
double? lat,
double? lng,
String? geohash,
double? long,
DateTime? createdAt,
int? visibility,
int? upvotes,
@ -177,13 +170,12 @@ class _QuoteImpl extends Quote {
Object? tags = _Undefined,
}) {
return Quote(
id: id ?? this.id,
id: id is int? ? id : this.id,
userId: userId ?? this.userId,
text: text ?? this.text,
authorName: authorName is String? ? authorName : this.authorName,
lat: lat ?? this.lat,
lng: lng ?? this.lng,
geohash: geohash ?? this.geohash,
long: long ?? this.long,
createdAt: createdAt ?? this.createdAt,
visibility: visibility ?? this.visibility,
upvotes: upvotes ?? this.upvotes,

View file

@ -6,3 +6,4 @@ environment:
dependencies:
serverpod_client: 2.9.1
serverpod_auth_server: ^2.9.1

View file

@ -27,6 +27,8 @@ dependencies:
serverpod_flutter: 2.9.1
wien_talks_client:
path: ../wien_talks_client
serverpod_auth_shared_flutter: ^2.9.1
# The following adds the Cupertino Icons font to your application.

View file

@ -11,7 +11,7 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod/serverpod.dart' as _i1;
import '../greeting_endpoint.dart' as _i2;
import '../quotes/location_endpoint.dart' as _i3;
import 'package:serverpod_auth_server/serverpod_auth_server.dart' as _i3;
class Endpoints extends _i1.EndpointDispatch {
@override
@ -22,13 +22,7 @@ class Endpoints extends _i1.EndpointDispatch {
server,
'greeting',
null,
),
'recipe': _i3.RecipeEndpoint()
..initialize(
server,
'recipe',
null,
),
)
};
connectors['greeting'] = _i1.EndpointConnector(
name: 'greeting',
@ -54,29 +48,6 @@ class Endpoints extends _i1.EndpointDispatch {
)
},
);
connectors['recipe'] = _i1.EndpointConnector(
name: 'recipe',
endpoint: endpoints['recipe']!,
methodConnectors: {
'postQuote': _i1.MethodConnector(
name: 'postQuote',
params: {
'quote': _i1.ParameterDescription(
name: 'quote',
type: _i1.getType<String>(),
nullable: false,
)
},
call: (
_i1.Session session,
Map<String, dynamic> params,
) async =>
(endpoints['recipe'] as _i3.RecipeEndpoint).postQuote(
session,
params['quote'],
),
)
},
);
modules['serverpod_auth'] = _i3.Endpoints()..initializeEndpoints(server);
}
}

View file

@ -11,9 +11,10 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod/serverpod.dart' as _i1;
import 'package:serverpod/protocol.dart' as _i2;
import 'greeting.dart' as _i3;
import 'quotes/create_quote.dart' as _i4;
import 'quotes/quote.dart' as _i5;
import 'package:serverpod_auth_server/serverpod_auth_server.dart' as _i3;
import 'greeting.dart' as _i4;
import 'quotes/create_quote.dart' as _i5;
import 'quotes/quote.dart' as _i6;
export 'greeting.dart';
export 'quotes/create_quote.dart';
export 'quotes/quote.dart';
@ -26,7 +27,100 @@ class Protocol extends _i1.SerializationManagerServer {
static final Protocol _instance = Protocol._();
static final List<_i2.TableDefinition> targetTableDefinitions = [
..._i2.Protocol.targetTableDefinitions
_i2.TableDefinition(
name: 'quote',
dartName: 'Quote',
schema: 'public',
module: 'wien_talks',
columns: [
_i2.ColumnDefinition(
name: 'id',
columnType: _i2.ColumnType.bigint,
isNullable: false,
dartType: 'int?',
columnDefault: 'nextval(\'quote_id_seq\'::regclass)',
),
_i2.ColumnDefinition(
name: 'userId',
columnType: _i2.ColumnType.bigint,
isNullable: false,
dartType: 'int',
),
_i2.ColumnDefinition(
name: 'text',
columnType: _i2.ColumnType.text,
isNullable: false,
dartType: 'String',
),
_i2.ColumnDefinition(
name: 'authorName',
columnType: _i2.ColumnType.text,
isNullable: true,
dartType: 'String?',
),
_i2.ColumnDefinition(
name: 'lat',
columnType: _i2.ColumnType.doublePrecision,
isNullable: false,
dartType: 'double',
),
_i2.ColumnDefinition(
name: 'long',
columnType: _i2.ColumnType.doublePrecision,
isNullable: false,
dartType: 'double',
),
_i2.ColumnDefinition(
name: 'createdAt',
columnType: _i2.ColumnType.timestampWithoutTimeZone,
isNullable: false,
dartType: 'DateTime',
),
_i2.ColumnDefinition(
name: 'visibility',
columnType: _i2.ColumnType.bigint,
isNullable: false,
dartType: 'int',
),
_i2.ColumnDefinition(
name: 'upvotes',
columnType: _i2.ColumnType.bigint,
isNullable: false,
dartType: 'int',
),
_i2.ColumnDefinition(
name: 'downvotes',
columnType: _i2.ColumnType.bigint,
isNullable: false,
dartType: 'int',
),
_i2.ColumnDefinition(
name: 'tags',
columnType: _i2.ColumnType.json,
isNullable: true,
dartType: 'List<String>?',
),
],
foreignKeys: [],
indexes: [
_i2.IndexDefinition(
indexName: 'quote_pkey',
tableSpace: null,
elements: [
_i2.IndexElementDefinition(
type: _i2.IndexElementDefinitionType.column,
definition: 'id',
)
],
type: 'btree',
isUnique: true,
isPrimary: true,
)
],
managed: true,
),
..._i3.Protocol.targetTableDefinitions,
..._i2.Protocol.targetTableDefinitions,
];
@override
@ -35,23 +129,23 @@ class Protocol extends _i1.SerializationManagerServer {
Type? t,
]) {
t ??= T;
if (t == _i3.Greeting) {
return _i3.Greeting.fromJson(data) as T;
if (t == _i4.Greeting) {
return _i4.Greeting.fromJson(data) as T;
}
if (t == _i4.CreateQuoteRequest) {
return _i4.CreateQuoteRequest.fromJson(data) as T;
if (t == _i5.CreateQuoteRequest) {
return _i5.CreateQuoteRequest.fromJson(data) as T;
}
if (t == _i5.Quote) {
return _i5.Quote.fromJson(data) as T;
if (t == _i6.Quote) {
return _i6.Quote.fromJson(data) as T;
}
if (t == _i1.getType<_i3.Greeting?>()) {
return (data != null ? _i3.Greeting.fromJson(data) : null) as T;
if (t == _i1.getType<_i4.Greeting?>()) {
return (data != null ? _i4.Greeting.fromJson(data) : null) as T;
}
if (t == _i1.getType<_i4.CreateQuoteRequest?>()) {
return (data != null ? _i4.CreateQuoteRequest.fromJson(data) : null) as T;
if (t == _i1.getType<_i5.CreateQuoteRequest?>()) {
return (data != null ? _i5.CreateQuoteRequest.fromJson(data) : null) as T;
}
if (t == _i1.getType<_i5.Quote?>()) {
return (data != null ? _i5.Quote.fromJson(data) : null) as T;
if (t == _i1.getType<_i6.Quote?>()) {
return (data != null ? _i6.Quote.fromJson(data) : null) as T;
}
if (t == _i1.getType<List<String>?>()) {
return (data != null
@ -63,6 +157,9 @@ class Protocol extends _i1.SerializationManagerServer {
? (data as List).map((e) => deserialize<String>(e)).toList()
: null) as T;
}
try {
return _i3.Protocol().deserialize<T>(data, t);
} on _i1.DeserializationTypeNotFoundException catch (_) {}
try {
return _i2.Protocol().deserialize<T>(data, t);
} on _i1.DeserializationTypeNotFoundException catch (_) {}
@ -73,19 +170,23 @@ class Protocol extends _i1.SerializationManagerServer {
String? getClassNameForObject(Object? data) {
String? className = super.getClassNameForObject(data);
if (className != null) return className;
if (data is _i3.Greeting) {
if (data is _i4.Greeting) {
return 'Greeting';
}
if (data is _i4.CreateQuoteRequest) {
if (data is _i5.CreateQuoteRequest) {
return 'CreateQuoteRequest';
}
if (data is _i5.Quote) {
if (data is _i6.Quote) {
return 'Quote';
}
className = _i2.Protocol().getClassNameForObject(data);
if (className != null) {
return 'serverpod.$className';
}
className = _i3.Protocol().getClassNameForObject(data);
if (className != null) {
return 'serverpod_auth.$className';
}
return null;
}
@ -96,29 +197,43 @@ class Protocol extends _i1.SerializationManagerServer {
return super.deserializeByClassName(data);
}
if (dataClassName == 'Greeting') {
return deserialize<_i3.Greeting>(data['data']);
return deserialize<_i4.Greeting>(data['data']);
}
if (dataClassName == 'CreateQuoteRequest') {
return deserialize<_i4.CreateQuoteRequest>(data['data']);
return deserialize<_i5.CreateQuoteRequest>(data['data']);
}
if (dataClassName == 'Quote') {
return deserialize<_i5.Quote>(data['data']);
return deserialize<_i6.Quote>(data['data']);
}
if (dataClassName.startsWith('serverpod.')) {
data['className'] = dataClassName.substring(10);
return _i2.Protocol().deserializeByClassName(data);
}
if (dataClassName.startsWith('serverpod_auth.')) {
data['className'] = dataClassName.substring(15);
return _i3.Protocol().deserializeByClassName(data);
}
return super.deserializeByClassName(data);
}
@override
_i1.Table? getTableForType(Type t) {
{
var table = _i3.Protocol().getTableForType(t);
if (table != null) {
return table;
}
}
{
var table = _i2.Protocol().getTableForType(t);
if (table != null) {
return table;
}
}
switch (t) {
case _i6.Quote:
return _i6.Quote.t;
}
return null;
}

View file

@ -11,16 +11,14 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:serverpod/serverpod.dart' as _i1;
abstract class Quote
implements _i1.SerializableModel, _i1.ProtocolSerialization {
abstract class Quote implements _i1.TableRow<int?>, _i1.ProtocolSerialization {
Quote._({
required this.id,
this.id,
required this.userId,
required this.text,
this.authorName,
required this.lat,
required this.lng,
required this.geohash,
required this.long,
required this.createdAt,
required this.visibility,
required this.upvotes,
@ -29,13 +27,12 @@ abstract class Quote
});
factory Quote({
required int id,
int? id,
required int userId,
required String text,
String? authorName,
required double lat,
required double lng,
required String geohash,
required double long,
required DateTime createdAt,
required int visibility,
required int upvotes,
@ -45,13 +42,12 @@ abstract class Quote
factory Quote.fromJson(Map<String, dynamic> jsonSerialization) {
return Quote(
id: jsonSerialization['id'] as int,
id: jsonSerialization['id'] as int?,
userId: jsonSerialization['userId'] as int,
text: jsonSerialization['text'] as String,
authorName: jsonSerialization['authorName'] as String?,
lat: (jsonSerialization['lat'] as num).toDouble(),
lng: (jsonSerialization['lng'] as num).toDouble(),
geohash: jsonSerialization['geohash'] as String,
long: (jsonSerialization['long'] as num).toDouble(),
createdAt:
_i1.DateTimeJsonExtension.fromJson(jsonSerialization['createdAt']),
visibility: jsonSerialization['visibility'] as int,
@ -63,7 +59,12 @@ abstract class Quote
);
}
int id;
static final t = QuoteTable();
static const db = QuoteRepository._();
@override
int? id;
int userId;
@ -73,9 +74,7 @@ abstract class Quote
double lat;
double lng;
String geohash;
double long;
DateTime createdAt;
@ -87,6 +86,9 @@ abstract class Quote
List<String>? tags;
@override
_i1.Table<int?> get table => t;
/// Returns a shallow copy of this [Quote]
/// with some or all fields replaced by the given arguments.
@_i1.useResult
@ -96,8 +98,7 @@ abstract class Quote
String? text,
String? authorName,
double? lat,
double? lng,
String? geohash,
double? long,
DateTime? createdAt,
int? visibility,
int? upvotes,
@ -107,13 +108,12 @@ abstract class Quote
@override
Map<String, dynamic> toJson() {
return {
'id': id,
if (id != null) 'id': id,
'userId': userId,
'text': text,
if (authorName != null) 'authorName': authorName,
'lat': lat,
'lng': lng,
'geohash': geohash,
'long': long,
'createdAt': createdAt.toJson(),
'visibility': visibility,
'upvotes': upvotes,
@ -125,13 +125,12 @@ abstract class Quote
@override
Map<String, dynamic> toJsonForProtocol() {
return {
'id': id,
if (id != null) 'id': id,
'userId': userId,
'text': text,
if (authorName != null) 'authorName': authorName,
'lat': lat,
'lng': lng,
'geohash': geohash,
'long': long,
'createdAt': createdAt.toJson(),
'visibility': visibility,
'upvotes': upvotes,
@ -140,6 +139,30 @@ abstract class Quote
};
}
static QuoteInclude include() {
return QuoteInclude._();
}
static QuoteIncludeList includeList({
_i1.WhereExpressionBuilder<QuoteTable>? where,
int? limit,
int? offset,
_i1.OrderByBuilder<QuoteTable>? orderBy,
bool orderDescending = false,
_i1.OrderByListBuilder<QuoteTable>? orderByList,
QuoteInclude? include,
}) {
return QuoteIncludeList._(
where: where,
limit: limit,
offset: offset,
orderBy: orderBy?.call(Quote.t),
orderDescending: orderDescending,
orderByList: orderByList?.call(Quote.t),
include: include,
);
}
@override
String toString() {
return _i1.SerializationManager.encode(this);
@ -150,13 +173,12 @@ class _Undefined {}
class _QuoteImpl extends Quote {
_QuoteImpl({
required int id,
int? id,
required int userId,
required String text,
String? authorName,
required double lat,
required double lng,
required String geohash,
required double long,
required DateTime createdAt,
required int visibility,
required int upvotes,
@ -168,8 +190,7 @@ class _QuoteImpl extends Quote {
text: text,
authorName: authorName,
lat: lat,
lng: lng,
geohash: geohash,
long: long,
createdAt: createdAt,
visibility: visibility,
upvotes: upvotes,
@ -182,13 +203,12 @@ class _QuoteImpl extends Quote {
@_i1.useResult
@override
Quote copyWith({
int? id,
Object? id = _Undefined,
int? userId,
String? text,
Object? authorName = _Undefined,
double? lat,
double? lng,
String? geohash,
double? long,
DateTime? createdAt,
int? visibility,
int? upvotes,
@ -196,13 +216,12 @@ class _QuoteImpl extends Quote {
Object? tags = _Undefined,
}) {
return Quote(
id: id ?? this.id,
id: id is int? ? id : this.id,
userId: userId ?? this.userId,
text: text ?? this.text,
authorName: authorName is String? ? authorName : this.authorName,
lat: lat ?? this.lat,
lng: lng ?? this.lng,
geohash: geohash ?? this.geohash,
long: long ?? this.long,
createdAt: createdAt ?? this.createdAt,
visibility: visibility ?? this.visibility,
upvotes: upvotes ?? this.upvotes,
@ -211,3 +230,326 @@ class _QuoteImpl extends Quote {
);
}
}
class QuoteTable extends _i1.Table<int?> {
QuoteTable({super.tableRelation}) : super(tableName: 'quote') {
userId = _i1.ColumnInt(
'userId',
this,
);
text = _i1.ColumnString(
'text',
this,
);
authorName = _i1.ColumnString(
'authorName',
this,
);
lat = _i1.ColumnDouble(
'lat',
this,
);
long = _i1.ColumnDouble(
'long',
this,
);
createdAt = _i1.ColumnDateTime(
'createdAt',
this,
);
visibility = _i1.ColumnInt(
'visibility',
this,
);
upvotes = _i1.ColumnInt(
'upvotes',
this,
);
downvotes = _i1.ColumnInt(
'downvotes',
this,
);
tags = _i1.ColumnSerializable(
'tags',
this,
);
}
late final _i1.ColumnInt userId;
late final _i1.ColumnString text;
late final _i1.ColumnString authorName;
late final _i1.ColumnDouble lat;
late final _i1.ColumnDouble long;
late final _i1.ColumnDateTime createdAt;
late final _i1.ColumnInt visibility;
late final _i1.ColumnInt upvotes;
late final _i1.ColumnInt downvotes;
late final _i1.ColumnSerializable tags;
@override
List<_i1.Column> get columns => [
id,
userId,
text,
authorName,
lat,
long,
createdAt,
visibility,
upvotes,
downvotes,
tags,
];
}
class QuoteInclude extends _i1.IncludeObject {
QuoteInclude._();
@override
Map<String, _i1.Include?> get includes => {};
@override
_i1.Table<int?> get table => Quote.t;
}
class QuoteIncludeList extends _i1.IncludeList {
QuoteIncludeList._({
_i1.WhereExpressionBuilder<QuoteTable>? where,
super.limit,
super.offset,
super.orderBy,
super.orderDescending,
super.orderByList,
super.include,
}) {
super.where = where?.call(Quote.t);
}
@override
Map<String, _i1.Include?> get includes => include?.includes ?? {};
@override
_i1.Table<int?> get table => Quote.t;
}
class QuoteRepository {
const QuoteRepository._();
/// Returns a list of [Quote]s matching the given query parameters.
///
/// Use [where] to specify which items to include in the return value.
/// If none is specified, all items will be returned.
///
/// To specify the order of the items use [orderBy] or [orderByList]
/// when sorting by multiple columns.
///
/// The maximum number of items can be set by [limit]. If no limit is set,
/// all items matching the query will be returned.
///
/// [offset] defines how many items to skip, after which [limit] (or all)
/// items are read from the database.
///
/// ```dart
/// var persons = await Persons.db.find(
/// session,
/// where: (t) => t.lastName.equals('Jones'),
/// orderBy: (t) => t.firstName,
/// limit: 100,
/// );
/// ```
Future<List<Quote>> find(
_i1.Session session, {
_i1.WhereExpressionBuilder<QuoteTable>? where,
int? limit,
int? offset,
_i1.OrderByBuilder<QuoteTable>? orderBy,
bool orderDescending = false,
_i1.OrderByListBuilder<QuoteTable>? orderByList,
_i1.Transaction? transaction,
}) async {
return session.db.find<Quote>(
where: where?.call(Quote.t),
orderBy: orderBy?.call(Quote.t),
orderByList: orderByList?.call(Quote.t),
orderDescending: orderDescending,
limit: limit,
offset: offset,
transaction: transaction,
);
}
/// Returns the first matching [Quote] matching the given query parameters.
///
/// Use [where] to specify which items to include in the return value.
/// If none is specified, all items will be returned.
///
/// To specify the order use [orderBy] or [orderByList]
/// when sorting by multiple columns.
///
/// [offset] defines how many items to skip, after which the next one will be picked.
///
/// ```dart
/// var youngestPerson = await Persons.db.findFirstRow(
/// session,
/// where: (t) => t.lastName.equals('Jones'),
/// orderBy: (t) => t.age,
/// );
/// ```
Future<Quote?> findFirstRow(
_i1.Session session, {
_i1.WhereExpressionBuilder<QuoteTable>? where,
int? offset,
_i1.OrderByBuilder<QuoteTable>? orderBy,
bool orderDescending = false,
_i1.OrderByListBuilder<QuoteTable>? orderByList,
_i1.Transaction? transaction,
}) async {
return session.db.findFirstRow<Quote>(
where: where?.call(Quote.t),
orderBy: orderBy?.call(Quote.t),
orderByList: orderByList?.call(Quote.t),
orderDescending: orderDescending,
offset: offset,
transaction: transaction,
);
}
/// Finds a single [Quote] by its [id] or null if no such row exists.
Future<Quote?> findById(
_i1.Session session,
int id, {
_i1.Transaction? transaction,
}) async {
return session.db.findById<Quote>(
id,
transaction: transaction,
);
}
/// Inserts all [Quote]s in the list and returns the inserted rows.
///
/// The returned [Quote]s will have their `id` fields set.
///
/// This is an atomic operation, meaning that if one of the rows fails to
/// insert, none of the rows will be inserted.
Future<List<Quote>> insert(
_i1.Session session,
List<Quote> rows, {
_i1.Transaction? transaction,
}) async {
return session.db.insert<Quote>(
rows,
transaction: transaction,
);
}
/// Inserts a single [Quote] and returns the inserted row.
///
/// The returned [Quote] will have its `id` field set.
Future<Quote> insertRow(
_i1.Session session,
Quote row, {
_i1.Transaction? transaction,
}) async {
return session.db.insertRow<Quote>(
row,
transaction: transaction,
);
}
/// Updates all [Quote]s in the list and returns the updated rows. If
/// [columns] is provided, only those columns will be updated. Defaults to
/// all columns.
/// This is an atomic operation, meaning that if one of the rows fails to
/// update, none of the rows will be updated.
Future<List<Quote>> update(
_i1.Session session,
List<Quote> rows, {
_i1.ColumnSelections<QuoteTable>? columns,
_i1.Transaction? transaction,
}) async {
return session.db.update<Quote>(
rows,
columns: columns?.call(Quote.t),
transaction: transaction,
);
}
/// Updates a single [Quote]. The row needs to have its id set.
/// Optionally, a list of [columns] can be provided to only update those
/// columns. Defaults to all columns.
Future<Quote> updateRow(
_i1.Session session,
Quote row, {
_i1.ColumnSelections<QuoteTable>? columns,
_i1.Transaction? transaction,
}) async {
return session.db.updateRow<Quote>(
row,
columns: columns?.call(Quote.t),
transaction: transaction,
);
}
/// Deletes all [Quote]s in the list and returns the deleted rows.
/// This is an atomic operation, meaning that if one of the rows fail to
/// be deleted, none of the rows will be deleted.
Future<List<Quote>> delete(
_i1.Session session,
List<Quote> rows, {
_i1.Transaction? transaction,
}) async {
return session.db.delete<Quote>(
rows,
transaction: transaction,
);
}
/// Deletes a single [Quote].
Future<Quote> deleteRow(
_i1.Session session,
Quote row, {
_i1.Transaction? transaction,
}) async {
return session.db.deleteRow<Quote>(
row,
transaction: transaction,
);
}
/// Deletes all rows matching the [where] expression.
Future<List<Quote>> deleteWhere(
_i1.Session session, {
required _i1.WhereExpressionBuilder<QuoteTable> where,
_i1.Transaction? transaction,
}) async {
return session.db.deleteWhere<Quote>(
where: where(Quote.t),
transaction: transaction,
);
}
/// Counts the number of rows matching the [where] expression. If omitted,
/// will return the count of all rows in the table.
Future<int> count(
_i1.Session session, {
_i1.WhereExpressionBuilder<QuoteTable>? where,
int? limit,
_i1.Transaction? transaction,
}) async {
return session.db.count<Quote>(
where: where?.call(Quote.t),
limit: limit,
transaction: transaction,
);
}
}

View file

@ -1,12 +0,0 @@
import 'dart:async';
import 'package:serverpod/serverpod.dart';
class RecipeEndpoint extends Endpoint {
Future<String> postQuote(Session session, String quote) async {
// validate content
// persist quote
return Future.value('none');
}
}

View file

@ -1,12 +1,12 @@
class: Quote
table: quote
fields:
id: int
id: int?
userId: int
text: String
authorName: String?
lat: double
lng: double
geohash: String
long: double
createdAt: DateTime
visibility: int
upvotes: int

View file

@ -0,0 +1,28 @@
import 'package:serverpod/serverpod.dart';
import 'package:wien_talks_server/src/generated/protocol.dart';
String validateQuote(CreateQuoteRequest req) {
final text = req.text.trim();
if (text.isEmpty || text.length > 500) {
throw FormatException('Text must be 1..500 chars');
}
if (req.lat.isNaN || req.lng.isNaN) {
throw FormatException('Invalid coordinates');
}
if (req.lat < -90 || req.lat > 90 || req.lng < -180 || req.lng > 180) {
throw FormatException('Coordinates out of bounds');
}
return text;
}
Future<List<Quote>> listNearby(
Session session, {
required double lat,
required double lng,
int radiusMeters = 1500,
int limit = 50,
}) async {
throw UnimplementedError();
}
enum Visibility { public, private, locallyPublic }

View file

@ -0,0 +1,43 @@
import 'package:serverpod/serverpod.dart';
import 'package:wien_talks_server/src/generated/protocol.dart';
import 'package:wien_talks_server/src/quotes/quote_controller.dart';
class QuoteEndpoint extends Endpoint {
Future<Quote> create(Session session, CreateQuoteRequest req) async {
final authInfo = await session.authenticated;
final userId = authInfo?.userId;
if (userId == null) {
throw Exception('Not signed in');
}
String text = validateQuote(req);
final quote = Quote(
id: 0,
userId: userId,
text: text,
authorName: req.authorName?.trim().isEmpty == true
? null
: req.authorName!.trim(),
lat: req.lat,
long: req.lng,
createdAt: DateTime.now().toUtc(),
visibility: 0,
upvotes: 0,
downvotes: 0,
);
final inserted = await session.db.insertRow<Quote>(quote);
return inserted;
}
Future<Quote> getQuoteById(Session session, int id) async {
final quote = await Quote.db.findById(session, id);
if (quote != null) {
return quote;
}
throw Exception('Quote not found');
}
}