Skip to content
Merged
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: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,6 @@ pubspec.lock
*.mocks.dart

# Ignore test coverage
coverage/
coverage/

/typesense-data
84 changes: 81 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,87 @@
# Contribution

[Feedback][feedback] and [Pull Requests][pr] are most welcome!
Thanks for your interest in contributing! [Feedback][feedback] and [Pull Requests][pr] are most welcome!

Also, you can join our [Slack Community][invite] and say hello 👋️!
Also, you can join our [Slack Community][invite] and say hello 👋️!

[feedback]: https://github.com/typesense/typesense-dart/issues/new/choose
[pr]: https://github.com/typesense/typesense-dart/pulls
[invite]: https://join.slack.com/t/typesense-community/shared_invite/zt-mx4nbsbn-AuOL89O7iBtvkz136egSJg
[invite]: https://join.slack.com/t/typesense-community/shared_invite/zt-mx4nbsbn-AuOL89O7iBtvkz136egSJg

## Prerequisites

- Dart SDK 3.6.1+
- A running Typesense server for testing (Docker is recommended)

You can override the default testing server connection via the following environment variables:

| Variable | Default |
| -------------------- | ----------- |
| `TYPESENSE_HOST` | `127.0.0.1` |
| `TYPESENSE_PORT` | `8108` |
| `TYPESENSE_PROTOCOL` | `http` |
| `TYPESENSE_API_KEY` | `xyz` |

## Getting started

Clone the repository:

```sh
git clone https://github.com/typesense/typesense-dart.git
cd typesense-dart
```

Install dependencies:

```sh
dart pub get
```

## Running tests

Generate test mocks:

```shell
dart run build_runner build
```

Start the local Typesense server (via Docker):

```sh
docker compose up -d
```

Run all tests:

```sh
dart test
```

Stop the Typesense server:

```sh
docker compose down
```

## Formatting

```sh
dart format .
```

## Static analysis

```sh
dart analyze
```

## Before submitting

Please ensure:

- All tests pass
- Code is formatted `dart format .`
- `dart analyze` reports no issues
- New features include tests

Thank you!
9 changes: 9 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
services:
typesense:
image: typesense/typesense:30.2
restart: on-failure
ports:
- '8108:8108'
volumes:
- ./typesense-data:/data
command: '--data-dir /data --api-key=xyz --enable-cors'
4 changes: 2 additions & 2 deletions lib/src/curation_sets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class CurationSets {
Future<List<CurationSetsListEntrySchema>> retrieve() async {
final response = await _apiCall.getList(resourcepath);
return response
.map((item) =>
CurationSetsListEntrySchema.fromJson(Map<String, dynamic>.from(item)))
.map((item) => CurationSetsListEntrySchema.fromJson(
Map<String, dynamic>.from(item)))
.toList();
}

Expand Down
11 changes: 6 additions & 5 deletions lib/src/models/analytics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ class AnalyticsEventsRetrieveSchema {
factory AnalyticsEventsRetrieveSchema.fromJson(Map<String, dynamic> json) =>
AnalyticsEventsRetrieveSchema(
events: (json['events'] as List? ?? [])
.map((item) =>
AnalyticsEventItemSchema.fromJson(Map<String, dynamic>.from(item as Map)))
.map((item) => AnalyticsEventItemSchema.fromJson(
Map<String, dynamic>.from(item as Map)))
.toList(),
);
}
Expand Down Expand Up @@ -96,7 +96,8 @@ class AnalyticsStatus {
this.docCounterEvents,
});

factory AnalyticsStatus.fromJson(Map<String, dynamic> json) => AnalyticsStatus(
factory AnalyticsStatus.fromJson(Map<String, dynamic> json) =>
AnalyticsStatus(
popularPrefixQueries: json['popular_prefix_queries'] as int?,
nohitsPrefixQueries: json['nohits_prefix_queries'] as int?,
logPrefixQueries: json['log_prefix_queries'] as int?,
Expand Down Expand Up @@ -260,8 +261,8 @@ class AnalyticsRulesRetrieveSchema {
factory AnalyticsRulesRetrieveSchema.fromJson(Map<String, dynamic> json) =>
AnalyticsRulesRetrieveSchema(
rules: (json['rules'] as List? ?? [])
.map((item) =>
AnalyticsRuleSchema.fromJson(Map<String, dynamic>.from(item as Map)))
.map((item) => AnalyticsRuleSchema.fromJson(
Map<String, dynamic>.from(item as Map)))
.toList(),
);
}
4 changes: 2 additions & 2 deletions lib/src/models/conversations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ class ConversationsRetrieveSchema {
factory ConversationsRetrieveSchema.fromJson(Map<String, dynamic> json) =>
ConversationsRetrieveSchema(
conversations: (json['conversations'] as List? ?? [])
.map((item) =>
ConversationSchema.fromJson(Map<String, dynamic>.from(item as Map)))
.map((item) => ConversationSchema.fromJson(
Map<String, dynamic>.from(item as Map)))
.toList(),
);
}
17 changes: 10 additions & 7 deletions lib/src/models/curation_sets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,10 @@ class CurationObjectSchema {
Map<String, dynamic> toJson() => {
'id': id,
if (rule != null) 'rule': rule!.toJson(),
if (includes != null) 'includes': includes!.map((e) => e.toJson()).toList(),
if (excludes != null) 'excludes': excludes!.map((e) => e.toJson()).toList(),
if (includes != null)
'includes': includes!.map((e) => e.toJson()).toList(),
if (excludes != null)
'excludes': excludes!.map((e) => e.toJson()).toList(),
if (filterBy != null) 'filter_by': filterBy,
if (sortBy != null) 'sort_by': sortBy,
if (replaceQuery != null) 'replace_query': replaceQuery,
Expand Down Expand Up @@ -230,8 +232,8 @@ class CurationSetSchema {
CurationSetSchema(
name: json['name'] as String?,
items: (json['items'] as List? ?? [])
.map((item) =>
CurationObjectSchema.fromJson(Map<String, dynamic>.from(item as Map)))
.map((item) => CurationObjectSchema.fromJson(
Map<String, dynamic>.from(item as Map)))
.toList(),
);
}
Expand All @@ -249,8 +251,8 @@ class CurationSetsListEntrySchema {
CurationSetsListEntrySchema(
name: json['name'] as String,
items: (json['items'] as List? ?? [])
.map((item) =>
CurationObjectSchema.fromJson(Map<String, dynamic>.from(item as Map)))
.map((item) => CurationObjectSchema.fromJson(
Map<String, dynamic>.from(item as Map)))
.toList(),
);
}
Expand All @@ -271,7 +273,8 @@ class CurationItemDeleteResponseSchema {

CurationItemDeleteResponseSchema({required this.id});

factory CurationItemDeleteResponseSchema.fromJson(Map<String, dynamic> json) =>
factory CurationItemDeleteResponseSchema.fromJson(
Map<String, dynamic> json) =>
CurationItemDeleteResponseSchema(
id: json['id'] as String,
);
Expand Down
3 changes: 1 addition & 2 deletions lib/src/models/stopwords.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ class StopwordSchema {
factory StopwordSchema.fromJson(Map<String, dynamic> json) {
final dynamic stopwordsValue = json['stopwords'];
if (!json.containsKey('id') && stopwordsValue is Map) {
return StopwordSchema.fromJson(
Map<String, dynamic>.from(stopwordsValue));
return StopwordSchema.fromJson(Map<String, dynamic>.from(stopwordsValue));
}

return StopwordSchema(
Expand Down
7 changes: 3 additions & 4 deletions lib/src/services/api_call.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,9 @@ class ApiCall extends BaseApiCall<Map<String, dynamic>> {
/// raised.
@override
@override
Map<String, dynamic> decode(String responseBody) =>
responseBody.isEmpty
? {}
: json.decode(responseBody) as Map<String, dynamic>;
Map<String, dynamic> decode(String responseBody) => responseBody.isEmpty
? {}
: json.decode(responseBody) as Map<String, dynamic>;

List<dynamic> decodeList(String responseBody) =>
responseBody.isEmpty ? [] : json.decode(responseBody) as List<dynamic>;
Expand Down
1 change: 0 additions & 1 deletion lib/src/services/request_cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ class RequestCache {

bool _isCacheValidList(_CachedListResult cache) =>
cache.validTill.difference(DateTime.now()) > Duration.zero;

}

class _CachedResult {
Expand Down
4 changes: 2 additions & 2 deletions lib/src/synonyms.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class Synonyms {
Future<Map<String, dynamic>> upsert(
String synonymId, Map<String, dynamic> params) async {
_warnDeprecated();
final response = await _apiCall.put('$_endpointPath/$synonymId',
bodyParameters: params);
final response =
await _apiCall.put('$_endpointPath/$synonymId', bodyParameters: params);
return Map<String, dynamic>.from(response);
}

Expand Down
65 changes: 32 additions & 33 deletions test/analytics_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ void main() {
final host = env['TYPESENSE_HOST'] ?? '127.0.0.1';
final port = int.tryParse(env['TYPESENSE_PORT'] ?? '8108') ?? 8108;
final protocolValue = env['TYPESENSE_PROTOCOL'] ?? 'http';
final protocol =
protocolValue == 'https' ? Protocol.https : Protocol.http;
final protocol = protocolValue == 'https' ? Protocol.https : Protocol.http;

late Client client;
const companiesCollection = 'companies';
Expand Down Expand Up @@ -48,28 +47,28 @@ void main() {

test('analytics rules create, update, retrieve, delete', () async {
final created = await client.analytics.rules().create(
AnalyticsRuleCreateSchema(
name: ruleName,
type: 'nohits_queries',
collection: companiesCollection,
eventType: 'search',
params: AnalyticsRuleParams(
destinationCollection: queriesCollection,
limit: 1000,
),
),
);
AnalyticsRuleCreateSchema(
name: ruleName,
type: 'nohits_queries',
collection: companiesCollection,
eventType: 'search',
params: AnalyticsRuleParams(
destinationCollection: queriesCollection,
limit: 1000,
),
),
);
expect(created, isA<AnalyticsRuleSchema>());

final updated = await client.analytics.rules().upsert(
ruleName,
AnalyticsRuleUpsertSchema(
params: AnalyticsRuleParams(
destinationCollection: queriesCollection,
limit: 500,
),
),
);
ruleName,
AnalyticsRuleUpsertSchema(
params: AnalyticsRuleParams(
destinationCollection: queriesCollection,
limit: 500,
),
),
);
expect(updated, isA<AnalyticsRuleSchema>());
expect(updated.name, equals(ruleName));

Expand Down Expand Up @@ -132,14 +131,14 @@ void main() {
} catch (_) {}

await client.analytics.rules().create(
AnalyticsRuleCreateSchema(
name: ruleName,
type: 'log',
collection: companiesCollection,
eventType: 'click',
params: AnalyticsRuleParams(),
),
);
AnalyticsRuleCreateSchema(
name: ruleName,
type: 'log',
collection: companiesCollection,
eventType: 'click',
params: AnalyticsRuleParams(),
),
);

final event = AnalyticsEventCreateSchema(
name: ruleName,
Expand All @@ -153,10 +152,10 @@ void main() {
expect(created.ok, isTrue);

final retrieved = await client.analytics.events().retrieve(
userId: 'user-1',
name: ruleName,
n: 10,
);
userId: 'user-1',
name: ruleName,
n: 10,
);
expect(retrieved, isA<AnalyticsEventsRetrieveSchema>());

final status = await client.analytics.events().status();
Expand Down
25 changes: 11 additions & 14 deletions test/conversations_models_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ void main() {
final host = env['TYPESENSE_HOST'] ?? '127.0.0.1';
final port = int.tryParse(env['TYPESENSE_PORT'] ?? '8108') ?? 8108;
final protocolValue = env['TYPESENSE_PROTOCOL'] ?? 'http';
final protocol =
protocolValue == 'https' ? Protocol.https : Protocol.http;
final protocol = protocolValue == 'https' ? Protocol.https : Protocol.http;
final openAiApiKey = env['OPENAI_API_KEY'];
final historyCollection =
env['CONVERSATION_HISTORY_COLLECTION'] ?? 'conversations_history';
Expand Down Expand Up @@ -61,8 +60,7 @@ void main() {
final created = await client.conversationsModels.create(
ConversationModelCreateSchema(
id: modelId,
modelName:
env['CONVERSATION_MODEL_NAME'] ?? 'openai/gpt-3.5-turbo',
modelName: env['CONVERSATION_MODEL_NAME'] ?? 'openai/gpt-3.5-turbo',
apiKey: openAiApiKey,
maxBytes: 16384,
historyCollection: historyCollection,
Expand All @@ -76,16 +74,15 @@ void main() {
expect(retrieved.id, equals(modelId));

final updated = await client.conversationModel(modelId).update(
ConversationModelCreateSchema(
id: modelId,
modelName:
env['CONVERSATION_MODEL_NAME'] ?? 'openai/gpt-3.5-turbo',
apiKey: openAiApiKey,
maxBytes: 16384,
systemPrompt: 'This is meant for testing purposes',
historyCollection: historyCollection,
),
);
ConversationModelCreateSchema(
id: modelId,
modelName: env['CONVERSATION_MODEL_NAME'] ?? 'openai/gpt-3.5-turbo',
apiKey: openAiApiKey,
maxBytes: 16384,
systemPrompt: 'This is meant for testing purposes',
historyCollection: historyCollection,
),
);
expect(updated, isA<ConversationModelCreateSchema>());

final list = await client.conversationsModels.retrieve();
Expand Down
Loading
Loading