From ea65fac01920252e50ccb0fab68bb62707617a05 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Wed, 16 Aug 2023 14:39:46 +0300 Subject: [PATCH 01/23] add code coverage --- .github/workflows/tests.yml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 036cf28..f8e172a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,10 +18,11 @@ jobs: # Will still run for each push to bump-meilisearch-v* if: github.event_name != 'pull_request' || !startsWith(github.base_ref, 'bump-meilisearch-v') timeout-minutes: 10 - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: + os: [ ubuntu-latest, macos-latest, windows-latest ] version: ['3.0.0'] name: integration-tests (dart ${{ matrix.version }}) @@ -40,9 +41,19 @@ jobs: with: sdk: ${{ matrix.version }} - name: Install dependencies - run: dart pub get + run: | + dart pub get + dart pub global activate coverage - name: Run integration tests - run: dart test --concurrency=4 + run: dart test --concurrency=4 --reporter=github --coverage=./coverage/reports + - name: Generate coverage reports + run: dart pub global run coverage:format_coverage --report-on=./lib --lcov --in=./coverage/reports --out=coverage/lcov.info + - name: Report to Codecov + uses: codecov/codecov-action@v3 + with: + file: coverage/lcov.info + fail_ci_if_error: true + linter: name: linter-check From e5fdad3da1df5b37dcd956e941c7fe8378f01fe3 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:04:16 +0300 Subject: [PATCH 02/23] initial code-excerpts --- .github/workflows/tests.yml | 14 +- test/code_samples.dart | 26 ++++ tool/.gitignore | 3 + tool/analysis_options.yaml | 18 +++ tool/bin/meili.dart | 1 + tool/lib/src/command_base.dart | 37 ++++++ tool/lib/src/core.dart | 16 +++ tool/lib/src/main.dart | 52 ++++++++ tool/lib/src/output_utils.dart | 44 ++++++ tool/lib/src/result.dart | 34 +++++ tool/lib/src/update_samples_command.dart | 162 +++++++++++++++++++++++ tool/pubspec.yaml | 29 ++++ 12 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 test/code_samples.dart create mode 100644 tool/.gitignore create mode 100644 tool/analysis_options.yaml create mode 100644 tool/bin/meili.dart create mode 100644 tool/lib/src/command_base.dart create mode 100644 tool/lib/src/core.dart create mode 100644 tool/lib/src/main.dart create mode 100644 tool/lib/src/output_utils.dart create mode 100644 tool/lib/src/result.dart create mode 100644 tool/lib/src/update_samples_command.dart create mode 100644 tool/pubspec.yaml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8c96150..32cfb47 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -74,7 +74,19 @@ jobs: uses: ibiqlik/action-yamllint@v3 with: config_file: .yamllint.yml - + + check-code-samples: + name: check .code-samples.meilisearch.yaml + runs-on: ubuntu-latest + needs: yaml-lint + steps: + - uses: actions/checkout@v3 + - uses: dart-lang/setup-dart@v1 + with: + sdk: '3.0.0' + - name: check if samples changed + run: | + dart run ./tool/bin/meili.dart update-samples --fail-on-change pana: runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/test/code_samples.dart b/test/code_samples.dart new file mode 100644 index 0000000..9007be5 --- /dev/null +++ b/test/code_samples.dart @@ -0,0 +1,26 @@ +import 'package:meilisearch/meilisearch.dart'; +import 'package:test/test.dart'; + +import 'utils/books.dart'; +import 'utils/client.dart'; + +void main() { + // this file hosts some code samples referenced in + // .code-samples.meilisearch.yaml + // it's subject to tests, lint rules, deprecation notices, etc... + group('code samples', () { + setUpClient(); + setUp(() async { + await createIndexWithData(uid: 'movies', data: [ + {'name': 'dragon'} + ]); + }); + test('code sample', () async { + // #docregion search_parameter_guide_show_ranking_score_1 + await client + .index('movies') + .search('dragon', SearchQuery(showRankingScore: true)); + // #enddocregion + }); + }); +} diff --git a/tool/.gitignore b/tool/.gitignore new file mode 100644 index 0000000..3a85790 --- /dev/null +++ b/tool/.gitignore @@ -0,0 +1,3 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ diff --git a/tool/analysis_options.yaml b/tool/analysis_options.yaml new file mode 100644 index 0000000..905bbd9 --- /dev/null +++ b/tool/analysis_options.yaml @@ -0,0 +1,18 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +linter: + rules: + avoid_print: false \ No newline at end of file diff --git a/tool/bin/meili.dart b/tool/bin/meili.dart new file mode 100644 index 0000000..4490950 --- /dev/null +++ b/tool/bin/meili.dart @@ -0,0 +1 @@ +export 'package:meili_tool/src/main.dart'; \ No newline at end of file diff --git a/tool/lib/src/command_base.dart b/tool/lib/src/command_base.dart new file mode 100644 index 0000000..bd7853d --- /dev/null +++ b/tool/lib/src/command_base.dart @@ -0,0 +1,37 @@ +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:meili_tool/src/result.dart'; +import 'package:platform/platform.dart'; +import 'package:path/path.dart' as p; + +abstract class MeiliCommandBase extends Command { + final Directory packageDirectory; + + MeiliCommandBase( + this.packageDirectory, { + this.platform = const LocalPlatform(), + }); + + /// The current platform. + /// + /// This can be overridden for testing. + final Platform platform; + + /// A context that matches the default for [platform]. + p.Context get path => platform.isWindows ? p.windows : p.posix; + // Returns the relative path from [from] to [entity] in Posix style. + /// + /// This should be used when, for example, printing package-relative paths in + /// status or error messages. + String getRelativePosixPath( + FileSystemEntity entity, { + required Directory from, + }) => + p.posix.joinAll(path.split(path.relative(entity.path, from: from.path))); + + String get indentation => ' '; + + bool getBoolArg(String key) { + return (argResults![key] as bool?) ?? false; + } +} diff --git a/tool/lib/src/core.dart b/tool/lib/src/core.dart new file mode 100644 index 0000000..9835d0a --- /dev/null +++ b/tool/lib/src/core.dart @@ -0,0 +1,16 @@ +/// Error thrown when a command needs to exit with a non-zero exit code. +/// +/// While there is no specific definition of the meaning of different non-zero +/// exit codes for this tool, commands should follow the general convention: +/// 1: The command ran correctly, but found errors. +/// 2: The command failed to run because the arguments were invalid. +/// >2: The command failed to run correctly for some other reason. Ideally, +/// each such failure should have a unique exit code within the context of +/// that command. +class ToolExit extends Error { + /// Creates a tool exit with the given [exitCode]. + ToolExit(this.exitCode); + + /// The code that the process should exit with. + final int exitCode; +} diff --git a/tool/lib/src/main.dart b/tool/lib/src/main.dart new file mode 100644 index 0000000..5432acf --- /dev/null +++ b/tool/lib/src/main.dart @@ -0,0 +1,52 @@ +import 'dart:io' as io; + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/local.dart'; +import 'package:meili_tool/src/output_utils.dart'; +import 'package:meili_tool/src/result.dart'; + +import 'core.dart'; +import 'update_samples_command.dart'; + +void main(List arguments) { + const FileSystem fileSystem = LocalFileSystem(); + final Directory scriptDir = + fileSystem.file(io.Platform.script.toFilePath()).parent; + final Directory toolsDir = + scriptDir.basename == 'bin' ? scriptDir.parent : scriptDir.parent.parent; + + final Directory meilisearchDirectory = toolsDir.parent; + + final commandRunner = CommandRunner( + 'dart run ./tool/bin/meili.dart', 'Productivity utils for meilisearch.') + ..addCommand(UpdateSamplesCommand(meilisearchDirectory)); + + commandRunner.run(arguments).then((value) { + if (value == null) { + print('MUST output either a success or fail.'); + assert(false); + io.exit(255); + } + switch (value.state) { + case RunState.succeeded: + printSuccess('Success!'); + break; + case RunState.failed: + printError('Failed!'); + if (value.details.isNotEmpty) { + printError(value.details.join('\n')); + } + } + }).catchError((Object e) { + final ToolExit toolExit = e as ToolExit; + int exitCode = toolExit.exitCode; + // This should never happen; this check is here to guarantee that a ToolExit + // never accidentally has code 0 thus causing CI to pass. + if (exitCode == 0) { + assert(false); + exitCode = 255; + } + io.exit(exitCode); + }, test: (Object e) => e is ToolExit); +} diff --git a/tool/lib/src/output_utils.dart b/tool/lib/src/output_utils.dart new file mode 100644 index 0000000..336c508 --- /dev/null +++ b/tool/lib/src/output_utils.dart @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:colorize/colorize.dart'; +import 'package:meta/meta.dart'; + +export 'package:colorize/colorize.dart' show Styles; + +/// True if color should be applied. +/// +/// Defaults to autodetecting stdout. +@visibleForTesting +bool useColorForOutput = stdout.supportsAnsiEscapes; + +String _colorizeIfAppropriate(String string, Styles color) { + if (!useColorForOutput) { + return string; + } + return Colorize(string).apply(color).toString(); +} + +/// Prints [message] in green, if the environment supports color. +void printSuccess(String message) { + print(_colorizeIfAppropriate(message, Styles.GREEN)); +} + +/// Prints [message] in yellow, if the environment supports color. +void printWarning(String message) { + print(_colorizeIfAppropriate(message, Styles.YELLOW)); +} + +/// Prints [message] in red, if the environment supports color. +void printError(String message) { + print(_colorizeIfAppropriate(message, Styles.RED)); +} + +/// Returns [message] with escapes to print it in [color], if the environment +/// supports color. +String colorizeString(String message, Styles color) { + return _colorizeIfAppropriate(message, color); +} \ No newline at end of file diff --git a/tool/lib/src/result.dart b/tool/lib/src/result.dart new file mode 100644 index 0000000..331bf43 --- /dev/null +++ b/tool/lib/src/result.dart @@ -0,0 +1,34 @@ +/// Possible outcomes of a command run for a package. +enum RunState { + /// The command succeeded for the package. + succeeded, + + /// The command failed for the package. + failed, +} + +/// The result of a [runForPackage] call. +class PackageResult { + /// A successful result. + PackageResult.success() : this._(RunState.succeeded); + + /// A run that failed. + /// + /// If [errors] are provided, they will be listed in the summary, otherwise + /// the summary will simply show that the package failed. + PackageResult.fail([List errors = const []]) + : this._(RunState.failed, errors); + + const PackageResult._(this.state, [this.details = const []]); + + /// The state the package run completed with. + final RunState state; + + /// Information about the result: + /// - For `succeeded`, this is empty. + /// - For `skipped`, it contains a single entry describing why the run was + /// skipped. + /// - For `failed`, it contains zero or more specific error details to be + /// shown in the summary. + final List details; +} diff --git a/tool/lib/src/update_samples_command.dart b/tool/lib/src/update_samples_command.dart new file mode 100644 index 0000000..6ca93fb --- /dev/null +++ b/tool/lib/src/update_samples_command.dart @@ -0,0 +1,162 @@ +// Source: https://github.com/flutter/packages/blob/d0411e450a8d94fcb221e8d8eacd3b1f8ca0e2fc/script/tool/lib/src/update_excerpts_command.dart +// but modified to accept yaml files. + +import 'dart:async'; + +import 'package:file/file.dart'; +import 'package:meili_tool/src/command_base.dart'; +import 'package:meili_tool/src/result.dart'; +import 'package:yaml/yaml.dart'; +import 'package:yaml_edit/yaml_edit.dart'; + +class _SourceFile { + final File file; + final List contents; + Map? result; + + _SourceFile({ + required this.file, + required this.contents, + }); +} + +class UpdateSamplesCommand extends MeiliCommandBase { + static const String _failOnChangeFlag = 'fail-on-change'; + + UpdateSamplesCommand( + super.packageDirectory, { + super.platform, + }) { + argParser.addFlag( + _failOnChangeFlag, + help: 'Fail if the command does anything. ' + '(Used in CI to ensure excerpts are up to date.)', + ); + } + + @override + String get description => + 'Updates .code-samples.meilisearch.yaml, based on code from code files'; + + @override + String get name => 'update-samples'; + + static const docregion = '#docregion'; + static const enddocregion = '#enddocregion'; + final startRegionRegex = RegExp(RegExp.escape(docregion) + r'\s+(?\w+)'); + + @override + Future run() async { + try { + final failOnChange = getBoolArg(_failOnChangeFlag); + //read the samples yaml file + final changedKeys = {}; + final File samplesFile = + packageDirectory.childFile('.code-samples.meilisearch.yaml'); + final samplesContentRaw = await samplesFile.readAsString(); + final samplesYaml = loadYaml(samplesContentRaw); + if (samplesYaml is! YamlMap) { + print(samplesYaml.runtimeType); + return PackageResult.fail(['samples yaml must be an YamlMap']); + } + final newSamplesYaml = YamlEditor(samplesContentRaw); + final sourceFiles = await _discoverSourceFiles(); + for (var sourceFile in sourceFiles) { + final newValues = _runInFile(sourceFile); + sourceFile.result = newValues; + for (var element in newValues.entries) { + final existingValue = samplesYaml[element.key]; + if (existingValue != null) { + if (existingValue == element.value) { + continue; + } else { + changedKeys[element.key] = element.value; + } + } else { + changedKeys[element.key] = element.value; + } + } + if (failOnChange && changedKeys.isNotEmpty) { + return PackageResult.fail([ + 'found changed keys: ${changedKeys.keys.toList()}', + ]); + } + if (!failOnChange) { + for (var changedEntry in changedKeys.entries) { + newSamplesYaml.update([changedEntry.key], changedEntry.value); + } + } + } + if (!failOnChange) { + await samplesFile.writeAsString(newSamplesYaml.toString()); + } + return PackageResult.success(); + } on PackageResult catch (e) { + return e; + } + } + + Map _runInFile(_SourceFile file) { + int lineNumber = 0; + String? currentKey; + final keys = []; + final res = {}; + final currentKeyLines = >[]; + for (var line in file.contents) { + lineNumber++; + if (currentKey == null) { + final capture = startRegionRegex.firstMatch(line); + if (capture == null) { + continue; + } + final key = capture.namedGroup('key'); + if (key == null) { + throw PackageResult.fail(['found a #docregion with no key']); + } + if (keys.contains(key)) { + throw PackageResult.fail(['found duplicate keys $key']); + } + keys.add(key); + currentKey = key; + } else { + if (line.contains(enddocregion)) { + final sb = StringBuffer(); + currentKeyLines.map((e) => e.value).forEach(sb.writeln); + //add to results. + res[currentKey] = sb.toString(); + } else { + currentKeyLines.add(MapEntry(lineNumber, line)); + } + } + } + return res; + } + + Future> _discoverSourceFiles() async { + final libDir = packageDirectory.childDirectory('lib'); + final testsDir = packageDirectory.childDirectory('test'); + //look in dart files and generate a new yaml file based on the referenced code. + final allDartFiles = [ + ...libDir.listSync(recursive: true), + ...testsDir.listSync(recursive: true), + ].where((element) => element.basename.toLowerCase().endsWith('.dart')); + + final sourceFiles = <_SourceFile>[]; + for (var dartFile in allDartFiles) { + if (dartFile is! File) { + continue; + } + final fileContents = await dartFile.readAsLines(); + if (!fileContents.any((line) => line.contains(docregion))) { + continue; + } + sourceFiles.add( + _SourceFile( + file: dartFile, + contents: fileContents, + ), + ); + } + return sourceFiles; + } +} diff --git a/tool/pubspec.yaml b/tool/pubspec.yaml new file mode 100644 index 0000000..c43c7c0 --- /dev/null +++ b/tool/pubspec.yaml @@ -0,0 +1,29 @@ +name: meili_tool +description: | + Productivity tools for meilisearch dart repository, + most of this is inspired from the flutter packages repository https://github.com/flutter/packages/. +version: 1.0.0 +repository: https://github.com/meilisearch/meilisearch-dart + +environment: + sdk: '>=3.0.0 <4.0.0' + +# Add regular dependencies here. +dependencies: + lints: ^2.0.0 + test: ^1.21.0 + args: ^2.4.2 + cli_util: ^0.4.0 + file: ^7.0.0 + path: ^1.8.3 + platform: ^3.1.2 + collection: ^1.15.0 + colorize: ^3.0.0 + meta: ^1.10.0 + yaml: ^3.1.2 + yaml_edit: ^2.1.1 + +dev_dependencies: + build_runner: ^2.0.3 + matcher: ^0.12.10 + mockito: '>=5.3.2 <=5.4.0' From 1614344d2f85b2d2339af5b786613d8c1e0feae7 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:13:10 +0300 Subject: [PATCH 03/23] don't fail CI if codecov fails --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 32cfb47..19755ee 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,7 +51,7 @@ jobs: uses: codecov/codecov-action@v3 with: file: coverage/lcov.info - fail_ci_if_error: true + fail_ci_if_error: false linter: name: linter-check From be80c7fb7aa8684a42b020ed18af3ab9fd2f65bf Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:17:57 +0300 Subject: [PATCH 04/23] yaml lint --- tool/analysis_options.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tool/analysis_options.yaml b/tool/analysis_options.yaml index 905bbd9..c0a9532 100644 --- a/tool/analysis_options.yaml +++ b/tool/analysis_options.yaml @@ -15,4 +15,4 @@ include: package:lints/recommended.yaml linter: rules: - avoid_print: false \ No newline at end of file + avoid_print: false From af7f4dd8a7ce263bff30cc3bb35e5eb1ee2b78f1 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:20:30 +0300 Subject: [PATCH 05/23] fix matrix --- .github/workflows/tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 19755ee..fccbd38 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,7 +24,7 @@ jobs: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] version: ['3.0.0'] - name: integration-tests (dart ${{ matrix.version }}) + name: integration-tests (dart ${{ matrix.version }}) (os ${{ matrix.os }}) services: meilisearch: image: getmeili/meilisearch:latest @@ -48,6 +48,7 @@ jobs: - name: Generate coverage reports run: dart pub global run coverage:format_coverage --report-on=./lib --lcov --in=./coverage/reports --out=coverage/lcov.info - name: Report to Codecov + if: ${{ matrix.os == 'linux'}} uses: codecov/codecov-action@v3 with: file: coverage/lcov.info From f0a428826e00c04efdf8d9aa3ab58d3f61ad6947 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:23:39 +0300 Subject: [PATCH 06/23] lints --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fccbd38..8dfd89b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -75,7 +75,7 @@ jobs: uses: ibiqlik/action-yamllint@v3 with: config_file: .yamllint.yml - + check-code-samples: name: check .code-samples.meilisearch.yaml runs-on: ubuntu-latest From 280d3231c92ba30eefad5d802d292ede9d560e67 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:24:08 +0300 Subject: [PATCH 07/23] don't depend on yaml lint --- .github/workflows/tests.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8dfd89b..dfdaa44 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -79,7 +79,6 @@ jobs: check-code-samples: name: check .code-samples.meilisearch.yaml runs-on: ubuntu-latest - needs: yaml-lint steps: - uses: actions/checkout@v3 - uses: dart-lang/setup-dart@v1 From e30bd093c009d351887a44083ba3c50f06d29337 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:28:15 +0300 Subject: [PATCH 08/23] pub get before running --- .github/workflows/tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dfdaa44..c65b085 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -86,6 +86,9 @@ jobs: sdk: '3.0.0' - name: check if samples changed run: | + pushd tool + dart pub get + popd dart run ./tool/bin/meili.dart update-samples --fail-on-change pana: runs-on: ubuntu-latest From 5c93135e4755e8d9a71f63871e7fc207f21df51d Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:31:09 +0300 Subject: [PATCH 09/23] run on ubuntu only. --- .github/workflows/tests.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c65b085..7219df8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,13 +18,12 @@ jobs: # Will still run for each push to bump-meilisearch-v* if: github.event_name != 'pull_request' || !startsWith(github.base_ref, 'bump-meilisearch-v') timeout-minutes: 10 - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - os: [ ubuntu-latest, macos-latest, windows-latest ] version: ['3.0.0'] - name: integration-tests (dart ${{ matrix.version }}) (os ${{ matrix.os }}) + name: integration-tests (dart ${{ matrix.version }}) services: meilisearch: image: getmeili/meilisearch:latest @@ -48,12 +47,10 @@ jobs: - name: Generate coverage reports run: dart pub global run coverage:format_coverage --report-on=./lib --lcov --in=./coverage/reports --out=coverage/lcov.info - name: Report to Codecov - if: ${{ matrix.os == 'linux'}} uses: codecov/codecov-action@v3 with: file: coverage/lcov.info fail_ci_if_error: false - linter: name: linter-check runs-on: ubuntu-latest From 433b277d8e142f85f41d8415e7de9b86157a8584 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:33:13 +0300 Subject: [PATCH 10/23] exit code 255 for failing. --- tool/lib/src/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/tool/lib/src/main.dart b/tool/lib/src/main.dart index 5432acf..ce3dc16 100644 --- a/tool/lib/src/main.dart +++ b/tool/lib/src/main.dart @@ -37,6 +37,7 @@ void main(List arguments) { if (value.details.isNotEmpty) { printError(value.details.join('\n')); } + io.exit(255); } }).catchError((Object e) { final ToolExit toolExit = e as ToolExit; From 851281b02b2ad7c1e2390d67c26b8f36b48a8679 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:39:35 +0300 Subject: [PATCH 11/23] run pub get in tool --- .github/workflows/pre-release-tests.yml | 2 +- .github/workflows/tests.yml | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pre-release-tests.yml b/.github/workflows/pre-release-tests.yml index a498df8..68d2419 100644 --- a/.github/workflows/pre-release-tests.yml +++ b/.github/workflows/pre-release-tests.yml @@ -27,4 +27,4 @@ jobs: - name: Meilisearch (${{ env.MEILISEARCH_VERSION }}) setup with Docker run: docker run -d -p 7700:7700 getmeili/meilisearch:${{ env.MEILISEARCH_VERSION }} meilisearch --master-key=masterKey --no-analytics - name: Run integration tests - run: docker run --net="host" -v $PWD:/package -w /package dart:${{ matrix.version }} /bin/sh -c 'dart pub get && dart run test' + run: docker run --net="host" -v $PWD:/package -w /package dart:${{ matrix.version }} /bin/sh -c 'dart pub get && dart pub get -C tool && dart run test' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7219df8..68e8873 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,6 +41,7 @@ jobs: - name: Install dependencies run: | dart pub get + dart pub get -C tool dart pub global activate coverage - name: Run integration tests run: dart test --concurrency=4 --reporter=github --coverage=./coverage/reports @@ -59,7 +60,9 @@ jobs: steps: - uses: actions/checkout@v3 - name: Install dependencies - run: dart pub get + run: | + dart pub get + dart pub get -C tool - name: Run linter run: dart analyze --fatal-infos && dart format . --set-exit-if-changed @@ -83,9 +86,8 @@ jobs: sdk: '3.0.0' - name: check if samples changed run: | - pushd tool dart pub get - popd + dart pub get -C tool dart run ./tool/bin/meili.dart update-samples --fail-on-change pana: runs-on: ubuntu-latest From ada94a4ca78d04b0a31937c397e24c69c801424e Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:40:59 +0300 Subject: [PATCH 12/23] format --- tool/bin/meili.dart | 2 +- tool/lib/src/output_utils.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tool/bin/meili.dart b/tool/bin/meili.dart index 4490950..201457b 100644 --- a/tool/bin/meili.dart +++ b/tool/bin/meili.dart @@ -1 +1 @@ -export 'package:meili_tool/src/main.dart'; \ No newline at end of file +export 'package:meili_tool/src/main.dart'; diff --git a/tool/lib/src/output_utils.dart b/tool/lib/src/output_utils.dart index 336c508..7fd39f6 100644 --- a/tool/lib/src/output_utils.dart +++ b/tool/lib/src/output_utils.dart @@ -41,4 +41,4 @@ void printError(String message) { /// supports color. String colorizeString(String message, Styles color) { return _colorizeIfAppropriate(message, color); -} \ No newline at end of file +} From f9109a0d7fa11f98e99bf694a79ff236e81f384e Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 12:43:39 +0300 Subject: [PATCH 13/23] fix key mismatch --- .code-samples.meilisearch.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 991d978..476adf0 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -3,8 +3,7 @@ # the documentation on build # You can read more on https://github.com/meilisearch/documentation/tree/master/.vuepress/code-samples --- -search_parameter_guide_show_ranking_score_1: |- - await client.index('movies').search('dragon', SearchQuery(showRankingScore: true)); +search_parameter_guide_show_ranking_score_1: " await client\n .index('movies')\n .search('dragon', SearchQuery(showRankingScore: true));\n" facet_search_2: |- await client.index('books').updateFaceting(Faceting(sortFacetValuesBy: {'genres': 'count'})); search_parameter_guide_attributes_to_search_on_1: |- From 9dc8b441de4e368ca787e7d04cb2d2539de59873 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 16:48:31 +0300 Subject: [PATCH 14/23] added functionality to detect missing and extra keys --- tool/lib/src/update_samples_command.dart | 34 ++++++++++++++++++++++++ tool/pubspec.yaml | 1 + 2 files changed, 35 insertions(+) diff --git a/tool/lib/src/update_samples_command.dart b/tool/lib/src/update_samples_command.dart index 6ca93fb..73c8abd 100644 --- a/tool/lib/src/update_samples_command.dart +++ b/tool/lib/src/update_samples_command.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'package:file/file.dart'; +import 'package:http/http.dart' as http; import 'package:meili_tool/src/command_base.dart'; import 'package:meili_tool/src/result.dart'; import 'package:yaml/yaml.dart'; @@ -59,6 +60,8 @@ class UpdateSamplesCommand extends MeiliCommandBase { print(samplesYaml.runtimeType); return PackageResult.fail(['samples yaml must be an YamlMap']); } + final fullSamplesYaml = await getFullCorrectSamples(); + final newSamplesYaml = YamlEditor(samplesContentRaw); final sourceFiles = await _discoverSourceFiles(); for (var sourceFile in sourceFiles) { @@ -87,6 +90,29 @@ class UpdateSamplesCommand extends MeiliCommandBase { } } } + + final missingEntries = fullSamplesYaml.entries + .where((element) => !samplesYaml.containsKey(element.key)); + final oldEntries = samplesYaml.entries + .where((element) => !fullSamplesYaml.containsKey(element.key)); + + if (failOnChange) { + if (missingEntries.isNotEmpty || oldEntries.isNotEmpty) { + return PackageResult.fail([ + if (missingEntries.isNotEmpty) + 'found the following missing entries: ${missingEntries.map((e) => e.key).join('\n')}', + if (oldEntries.isNotEmpty) + 'found the following useless entries: ${oldEntries.map((e) => e.key).join('\n')}', + ]); + } + } else { + for (var element in missingEntries) { + newSamplesYaml.update([element.key], element.value); + } + for (var element in oldEntries) { + newSamplesYaml.remove([element.key]); + } + } if (!failOnChange) { await samplesFile.writeAsString(newSamplesYaml.toString()); } @@ -96,6 +122,14 @@ class UpdateSamplesCommand extends MeiliCommandBase { } } + Future getFullCorrectSamples() async { + final uri = Uri.parse( + 'https://raw.githubusercontent.com/meilisearch/documentation/main/.code-samples.meilisearch.yaml'); + final data = await http.get(uri); + final parsed = loadYaml(data.body, sourceUrl: uri); + return parsed as YamlMap; + } + Map _runInFile(_SourceFile file) { int lineNumber = 0; String? currentKey; diff --git a/tool/pubspec.yaml b/tool/pubspec.yaml index c43c7c0..be5bf8b 100644 --- a/tool/pubspec.yaml +++ b/tool/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: meta: ^1.10.0 yaml: ^3.1.2 yaml_edit: ^2.1.1 + http: ^1.1.0 dev_dependencies: build_runner: ^2.0.3 From 0dc2c871b48db731380ac4b18837e930a8f6b65f Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 16:52:06 +0300 Subject: [PATCH 15/23] temp add keys --- .code-samples.meilisearch.yaml | 80 ++++++++-------------------------- 1 file changed, 19 insertions(+), 61 deletions(-) diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 476adf0..52c8624 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -39,24 +39,6 @@ multi_search_1: |- indexUid: "movies_ratings" ), ])); -faceted_search_2: |- - await client.multiSearch(MultiSearchQuery(queries: [ - IndexSearchQuery( - indexUid: 'books', - facets: ['language', 'genres', 'author', 'format'], - filter: '(language = English AND language = French) OR genres = Fiction' - ), - IndexSearchQuery( - indexUid: 'books', - facets: ['language'], - filter: 'genres = Fiction' - ), - IndexSearchQuery( - indexUid: "books", - facets: ['genres'], - filter: 'language = English OR language = French' - ), - ])); faceted_search_update_settings_1: |- await client.index('movie_ratings').updateFilterableAttributes(['genres', 'rating', 'language']); search_parameter_guide_facet_stats_1: |- @@ -83,8 +65,6 @@ delete_tasks_1: |- await client.deleteTasks(params: DeleteTasksQuery(uids: [1, 2])); cancel_tasks_1: |- await client.cancelTasks(params: CancelTasksQuery(uids: [1, 2])); -async_guide_canceled_by: |- - await client.getTasks(params: TasksQuery(canceledBy: [9])); swap_indexes_1: |- await client.swapIndexes([SwapIndex(['indexA', 'indexB']), SwapIndex(['indexX', 'indexY'])]); search_parameter_guide_hitsperpage_1: |- @@ -174,8 +154,6 @@ get_task_1: |- await client.getTask(1); get_all_tasks_1: |- await client.getTasks(); -get_keys_1: |- - await client.getKeys(); get_settings_1: |- await client.index('movies').getSettings(); update_settings_1: |- @@ -329,10 +307,6 @@ add_movies_json_1: |- final json = await File('movies.json').readAsString(); await client.index('movies').addDocumentsJson(json); -documents_guide_add_movie_1: |- - await client.index('movies').addDocuments([ - { 'movie_id': '123sq178', 'title': 'Amélie Poulain' } - ]); primary_field_guide_create_index_primary_key: |- await client.createIndex('books', primaryKey: 'reference_number'); primary_field_guide_update_document_primary_key: |- @@ -401,9 +375,6 @@ getting_started_update_displayed_attributes: |- 'overview', 'poster' ]); -getting_started_communicating_with_a_protected_instance: |- - var client = MeiliSearchClient('http://localhost:7700', 'apiKey'); - await client.index('movies').search(''); getting_started_add_meteorites: |- final json = await File('meteorites.json').readAsString(); @@ -435,8 +406,6 @@ filtering_update_settings_1: |- 'director', 'genres', ]); -faceted_search_facets_1: |- - await client.index('movies').search('Batman', SearchQuery(facets: ['genres'])); faceted_search_walkthrough_filter_1: |- await client.index('movies').search('thriller', SearchQuery(filter: [ ['genres = Horror', 'genres = Mystery'], @@ -530,36 +499,6 @@ security_guide_list_keys_1: |- security_guide_delete_key_1: |- var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); await client.deleteKey('ac5cd97d-5a4b-4226-a868-2d0eb6d197ab'); -tenant_token_guide_generate_sdk_1: |- - final uid = '85c3c2f9-bdd6-41f1-abd8-11fcf80e0f76'; - final apiKey = 'B5KdX2MY2jV6EXfUs6scSfmC...'; - final expiresAt = DateTime.utc(2025, 12, 20); - final searchRules = { - 'patient_medical_records': { - 'filter': 'user_id = 1' - } - }; - - final token = client.generateTenantToken( - uid, - searchRules, - apiKey: apiKey, // optional - expiresAt: expiresAt // optional - ); -tenant_token_guide_search_sdk_1: |- - final frontEndClient = MeiliSearchClient('http://localhost:7700', token); - await frontEndClient.index('patient_medical_records').search('blood test'); -landing_getting_started_1: |- - var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); - - await client.index('movies').addDocuments([ - { 'id': 1, 'title': 'Carol' }, - { 'id': 2, 'title': 'Wonder Woman' }, - { 'id': 3, 'title': 'Life of Pi' }, - { 'id': 4, 'title': 'Mad Max: Fury Road' }, - { 'id': 5, 'title': 'Moana' }, - { 'id': 6, 'title': 'Philadelphia'} - ]); search_parameter_guide_crop_marker_1: |- await client .index('movies') @@ -575,3 +514,22 @@ geosearch_guide_filter_usage_3: |- await client.index('restaurants') .search('', SearchQuery(filter: '_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])')), }) +search_get_1: "curl \\\n -X GET 'http:\/\/localhost:7700\/indexes\/movies\/search?q=american%20ninja'" +tenant_token_guide_search_no_sdk_1: "curl \\\n -X POST 'http:\/\/localhost:7700\/indexes\/patient_medical_records\/search' \\\n -H 'Authorization: Bearer TENANT_TOKEN'" +get_typo_tolerance_1: "curl \\\n -X GET 'http:\/\/localhost:7700\/indexes\/books\/settings\/typo-tolerance'" +update_typo_tolerance_1: "curl \\\n -X PATCH 'http:\/\/localhost:7700\/indexes\/books\/settings\/typo-tolerance' \\\n -H 'Content-Type: application\/json' \\\n --data-binary '{\n \"minWordSizeForTypos\": {\n \"oneTypo\": 4,\n \"twoTypos\": 10\n },\n \"disableOnAttributes\": [\"title\"]\n }'" +reset_typo_tolerance_1: "curl \\\n -X DELETE 'http:\/\/localhost:7700\/indexes\/books\/settings\/typo-tolerance'" +typo_tolerance_guide_1: "curl \\\n -X PATCH 'http:\/\/localhost:7700\/indexes\/movies\/settings\/typo-tolerance' \\\n -H 'Content-Type: application\/json' \\\n --data-binary '{ \"enabled\": false }'" +typo_tolerance_guide_2: "curl \\\n -X PATCH 'http:\/\/localhost:7700\/indexes\/movies\/settings\/typo-tolerance' \\\n -H 'Content-Type: application\/json' \\\n --data-binary '{ \"disableOnAttributes\": [\"title\"] }'" +typo_tolerance_guide_3: "curl \\\n -X PATCH 'http:\/\/localhost:7700\/indexes\/movies\/settings\/typo-tolerance' \\\n -H 'Content-Type: application\/json' \\\n --data-binary '{\n \"disableOnWords\": [\n \"shrek\"\n ]\n }'" +typo_tolerance_guide_4: "curl \\\n -X PATCH 'http:\/\/localhost:7700\/indexes\/movies\/settings\/typo-tolerance' \\\n -H 'Content-Type: application\/json' \\\n --data-binary '{\n \"minWordSizeForTypos\": {\n \"oneTypo\": 4,\n \"twoTypos\": 10\n }\n }'" +updating_guide_check_version_new_authorization_header: "curl \\\n -X GET 'http:\/\/\/version' \\\n -H 'Authorization: Bearer API_KEY'" +updating_guide_check_version_old_authorization_header: "curl \\\n -X GET 'http:\/\/\/version' \\\n -H 'X-Meili-API-Key: API_KEY'" +updating_guide_get_displayed_attributes_old_authorization_header: "# whenever you see {index_uid}, replace it with your index's unique id\ncurl \\\n -X GET 'http:\/\/\/indexes\/{index_uid}\/settings\/displayed-attributes' \\\n -H 'X-Meili-API-Key: API_KEY'" +updating_guide_reset_displayed_attributes_old_authorization_header: "curl \\\n -X DELETE 'http:\/\/\/indexes\/{index_uid}\/settings\/displayed-attributes' \\\n -H 'X-Meili-API-Key: API_KEY'" +updating_guide_create_dump: "curl \\\n -X POST 'http:\/\/\/dumps' \\\n -H 'Authorization: Bearer API_KEY'\n# -H 'X-Meili-API-Key: API_KEY' for v0.24 or below" +async_guide_canceled_by_1: "curl \\\n -X GET 'http:\/\/localhost:7700\/tasks?canceledBy=9,15'" +get_experimental_features_1: "curl \\\n -X GET 'http:\/\/localhost:7700\/experimental-features\/'" +update_experimental_features_1: "curl \\\n -X PATCH 'http:\/\/localhost:7700\/experimental-features\/' \\\n -H 'Content-Type: application\/json' \\\n --data-binary '{\n \"scoreDetails\": true\n }'" +facet_search_1: "curl \\\n -X POST 'http:\/\/localhost:7700\/indexes\/books\/facet-search' \\\n -H 'Content-Type: application\/json' \\\n --data-binary '{\n \"facetQuery\": \"fiction\",\n \"facetName\": \"genres\",\n \"filter\": \"rating > 3\"\n }'" +facet_search_3: "curl \\\n -X POST 'http:\/\/localhost:7700\/indexes\/books\/facet-search' \\\n -H 'Content-Type: application\/json' \\\n --data-binary '{\n \"facetQuery\": \"c\",\n \"facetName\": \"genres\"\n}'" From b93d36995ddef759a538963f7b9a1dc7f8ab0f0a Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 17:26:07 +0300 Subject: [PATCH 16/23] fix tool --- .code-samples.meilisearch.yaml | 4 +-- test/code_samples.dart | 14 ++++----- tool/lib/src/update_samples_command.dart | 40 +++++++++++++++++++----- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 52c8624..8cd6804 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -3,7 +3,7 @@ # the documentation on build # You can read more on https://github.com/meilisearch/documentation/tree/master/.vuepress/code-samples --- -search_parameter_guide_show_ranking_score_1: " await client\n .index('movies')\n .search('dragon', SearchQuery(showRankingScore: true));\n" +search_parameter_guide_show_ranking_score_1: "await client\n .index('movies')\n .search('dragon', SearchQuery(showRankingScore: true));" facet_search_2: |- await client.index('books').updateFaceting(Faceting(sortFacetValuesBy: {'genres': 'count'})); search_parameter_guide_attributes_to_search_on_1: |- @@ -514,7 +514,7 @@ geosearch_guide_filter_usage_3: |- await client.index('restaurants') .search('', SearchQuery(filter: '_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])')), }) -search_get_1: "curl \\\n -X GET 'http:\/\/localhost:7700\/indexes\/movies\/search?q=american%20ninja'" +search_get_1: " await client.index('movies').search('american ninja');" tenant_token_guide_search_no_sdk_1: "curl \\\n -X POST 'http:\/\/localhost:7700\/indexes\/patient_medical_records\/search' \\\n -H 'Authorization: Bearer TENANT_TOKEN'" get_typo_tolerance_1: "curl \\\n -X GET 'http:\/\/localhost:7700\/indexes\/books\/settings\/typo-tolerance'" update_typo_tolerance_1: "curl \\\n -X PATCH 'http:\/\/localhost:7700\/indexes\/books\/settings\/typo-tolerance' \\\n -H 'Content-Type: application\/json' \\\n --data-binary '{\n \"minWordSizeForTypos\": {\n \"oneTypo\": 4,\n \"twoTypos\": 10\n },\n \"disableOnAttributes\": [\"title\"]\n }'" diff --git a/test/code_samples.dart b/test/code_samples.dart index 9007be5..96677f6 100644 --- a/test/code_samples.dart +++ b/test/code_samples.dart @@ -9,18 +9,16 @@ void main() { // .code-samples.meilisearch.yaml // it's subject to tests, lint rules, deprecation notices, etc... group('code samples', () { - setUpClient(); - setUp(() async { - await createIndexWithData(uid: 'movies', data: [ - {'name': 'dragon'} - ]); - }); - test('code sample', () async { + test('excerpts', () async { // #docregion search_parameter_guide_show_ranking_score_1 await client .index('movies') .search('dragon', SearchQuery(showRankingScore: true)); // #enddocregion + + // #docregion search_get_1 + await client.index('movies').search('american ninja'); + // #enddocregion }); - }); + }, skip: true); } diff --git a/tool/lib/src/update_samples_command.dart b/tool/lib/src/update_samples_command.dart index 73c8abd..86199d3 100644 --- a/tool/lib/src/update_samples_command.dart +++ b/tool/lib/src/update_samples_command.dart @@ -2,7 +2,6 @@ // but modified to accept yaml files. import 'dart:async'; - import 'package:file/file.dart'; import 'package:http/http.dart' as http; import 'package:meili_tool/src/command_base.dart'; @@ -101,17 +100,19 @@ class UpdateSamplesCommand extends MeiliCommandBase { return PackageResult.fail([ if (missingEntries.isNotEmpty) 'found the following missing entries: ${missingEntries.map((e) => e.key).join('\n')}', - if (oldEntries.isNotEmpty) - 'found the following useless entries: ${oldEntries.map((e) => e.key).join('\n')}', + // for now don't delete old entries + // if (oldEntries.isNotEmpty) + // 'found the following useless entries: ${oldEntries.map((e) => e.key).join('\n')}', ]); } } else { for (var element in missingEntries) { newSamplesYaml.update([element.key], element.value); } - for (var element in oldEntries) { - newSamplesYaml.remove([element.key]); - } + // for now don't delete old entries + // for (var element in oldEntries) { + // newSamplesYaml.remove([element.key]); + // } } if (!failOnChange) { await samplesFile.writeAsString(newSamplesYaml.toString()); @@ -155,9 +156,16 @@ class UpdateSamplesCommand extends MeiliCommandBase { } else { if (line.contains(enddocregion)) { final sb = StringBuffer(); - currentKeyLines.map((e) => e.value).forEach(sb.writeln); + + unindentLines(currentKeyLines.map((e) => e.value).toList()) + .take(currentKeyLines.length - 1) + .forEach(sb.writeln); + sb.write(currentKeyLines.last.value); //add to results. res[currentKey] = sb.toString(); + + currentKey = null; + currentKeyLines.clear(); } else { currentKeyLines.add(MapEntry(lineNumber, line)); } @@ -166,6 +174,24 @@ class UpdateSamplesCommand extends MeiliCommandBase { return res; } + List unindentLines(List src) { + if (src.isEmpty) { + return src; + } + final ogFirst = src.first; + final trimmedFirst = ogFirst.trimLeft(); + final firstIndentation = ogFirst.length - trimmedFirst.length; + final res = []; + for (var element in src) { + final trimmedLine = element.trimLeft(); + var indentation = element.length - trimmedLine.length; + indentation -= firstIndentation; + res.add('${" " * indentation}$trimmedLine'); + } + + return res; + } + Future> _discoverSourceFiles() async { final libDir = packageDirectory.childDirectory('lib'); final testsDir = packageDirectory.childDirectory('test'); From b9aab226b5171497a5699a3550a80b827f215b50 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 19:31:06 +0300 Subject: [PATCH 17/23] added all samples --- .code-samples.meilisearch.yaml | 427 +++-------- test/code_samples.dart | 874 ++++++++++++++++++++++- test/documents_test.dart | 45 ++ test/multi_index_search_test.dart | 10 + test/search_test.dart | 98 +++ test/settings_test.dart | 15 + test/swaps_test.dart | 9 + test/tasks_test.dart | 56 ++ tool/lib/src/update_samples_command.dart | 101 ++- 9 files changed, 1256 insertions(+), 379 deletions(-) diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 4ea7090..2dc1af4 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -2,75 +2,25 @@ # Every example written here will be automatically fetched by # the documentation and the landing page on build --- -search_parameter_guide_show_ranking_score_1: "await client\n .index('movies')\n .search('dragon', SearchQuery(showRankingScore: true));" -facet_search_1: |- - await client.index('books').facetSearch(FacetSearchQuery( - facetQuery: 'fiction', - facetName: 'genres', - filter: 'rating > 3' - ), - ); -facet_search_2: |- - await client.index('books').updateFaceting(Faceting(sortFacetValuesBy: {'genres': 'count'})); -facet_search_3: |- - await client.index('books').facetSearch(FacetSearchQuery( - facetQuery: 'c', - facetName: 'genres', - ), - ); -search_parameter_guide_attributes_to_search_on_1: |- - await client.index('movies').search('adventure', SearchQuery(attributesToSearchOn: ['overview'])); -get_documents_post_1: |- - await client.index('movies').getDocuments( - params: DocumentsQuery( - filter: '(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English', - fields: ['title', 'genres', 'rating', 'language'], - limit: 3 - ) - ); -delete_documents_by_filter_1: |- - await client.index('movies') - .deleteDocuments(DeleteDocumentsQuery(filter: 'genres = action OR genres = adventure')); -get_documents_1: |- - await client.index('movies') - .getDocuments(params: DocumentsQuery(limit: 2, filter: 'genres = action')); -multi_search_1: |- - await client.multiSearch(MultiSearchQuery(queries: [ - IndexSearchQuery( - query: 'pooh', - indexUid: 'movies', - limit: 5 - ), - IndexSearchQuery( - query: 'nemo', - indexUid: 'movies', - limit: 5 - ), - IndexSearchQuery( - query: 'us', - indexUid: 'movies_ratings' - ), - ])); -faceted_search_update_settings_1: |- - await client.index('movie_ratings').updateFilterableAttributes(['genres', 'rating', 'language']); -search_parameter_guide_facet_stats_1: |- - await client.index('movie_ratings').search('Batman', SearchQuery(facets: ['genres', 'rating'])); -faceted_search_1: |- - await client.index('books').search('', SearchQuery(facets: ['genres', 'rating', 'language'])); -filtering_guide_nested_1: |- - await client.index('movie_ratings').search('thriller', SearchQuery(filter: 'rating.users >= 90')); -sorting_guide_sort_nested_1: |- - await client.index('movie_ratings').search('thriller', SearchQuery(sort: ['rating.users:asc'])); -async_guide_filter_by_date_1: |- - await client.getTasks(params: TasksQuery(afterEnqueuedAt: DateTime(2020, 10, 11, 11, 49, 53))); -async_guide_multiple_filters_1: |- - await client.getTasks(params: TasksQuery(indexUids: ['movies'], types: ['documentAdditionOrUpdate','documentDeletion'], statuses: ['processing'])); -async_guide_filter_by_ids_1: |- - await client.getTasks(params: TasksQuery(uids: [5, 10, 13])); -async_guide_filter_by_statuses_1: |- - await client.getTasks(params: TasksQuery(statuses: ['failed', 'canceled'])); -async_guide_filter_by_types_1: |- - await client.getTasks(params: TasksQuery(types: ['dumpCreation', 'indexSwap'])); +search_parameter_guide_show_ranking_score_1: "await client\n .index('movies')\n .search('dragon', SearchQuery(showRankingScore: true));" +facet_search_1: "await client.index('books').facetSearch(\n FacetSearchQuery(\n facetQuery: 'fiction',\n facetName: 'genres',\n filter: 'rating > 3',\n ),\n );" +facet_search_2: "await client.index('books').updateFaceting(\n Faceting(\n sortFacetValuesBy: {\n 'genres': FacetingSortTypes.count,\n },\n ),\n );" +facet_search_3: "await client.index('books').facetSearch(\n FacetSearchQuery(\n facetQuery: 'c',\n facetName: 'genres',\n ),\n );" +search_parameter_guide_attributes_to_search_on_1: "await client.index('books').facetSearch(\n FacetSearchQuery(\n facetQuery: 'c',\n facetName: 'genres',\n ),\n );" +get_documents_post_1: "await client.index('movies').getDocuments(\n params: DocumentsQuery(\n filterExpression: Meili.and([\n 'language'.toMeiliAttribute().eq('English'.toMeiliValue()),\n Meili.and([\n 'rating'.toMeiliAttribute().gt(3.toMeiliValue()),\n Meili.or([\n 'genres'.toMeiliAttribute().eq('Adventure'.toMeiliValue()),\n 'genres'.toMeiliAttribute().eq('Fiction'.toMeiliValue()),\n ]),\n ]),\n ]),\n fields: ['title', 'genres', 'rating', 'language'],\n limit: 3,\n ),\n );" +delete_documents_by_filter_1: "await client.index('movies').deleteDocuments(\n DeleteDocumentsQuery(\n filterExpression: Meili.or([\n Meili.attr('genres').eq(Meili.value('action')),\n Meili.attr('genres').eq(Meili.value('adventure')),\n ]),\n ),\n );" +get_documents_1: "await client.index('movies').getDocuments(\n params: DocumentsQuery(\n limit: 2,\n filter: Meili.attr('genres').eq('action'.toMeiliValue()),\n ),\n );" +multi_search_1: "await client.multiSearch(MultiSearchQuery(queries: [\n IndexSearchQuery(query: 'pooh', indexUid: 'movies', limit: 5),\n IndexSearchQuery(query: 'nemo', indexUid: 'movies', limit: 5),\n IndexSearchQuery(query: 'us', indexUid: 'movies_ratings'),\n]));" +faceted_search_update_settings_1: "await client\n .index('movie_ratings')\n .updateFilterableAttributes(['genres', 'rating', 'language']);" +search_parameter_guide_facet_stats_1: "await client\n .index('movie_ratings')\n .search('Batman', SearchQuery(facets: ['genres', 'rating']));" +faceted_search_1: "await client\n .index('books')\n .search('', SearchQuery(facets: ['genres', 'rating', 'language']));" +filtering_guide_nested_1: "await client.index('movie_ratings').search(\n 'thriller',\n SearchQuery(\n filterExpression: Meili.gte(\n \/\/or Meili.attr('rating.users')\n \/\/or 'rating.users'.toMeiliAttribute()\n Meili.attrFromParts(['rating', 'users']),\n Meili.value(90),\n ),\n ),\n );" +sorting_guide_sort_nested_1: "await client\n .index('movie_ratings')\n .search('thriller', SearchQuery(sort: ['rating.users:asc']));" +async_guide_filter_by_date_1: "await client.getTasks(\n params: TasksQuery(\n afterEnqueuedAt: DateTime(2020, 10, 11, 11, 49, 53),\n ),\n);" +async_guide_multiple_filters_1: "await client.getTasks(\n params: TasksQuery(\n indexUids: ['movies'],\n types: ['documentAdditionOrUpdate', 'documentDeletion'],\n statuses: ['processing'],\n ),\n);" +async_guide_filter_by_ids_1: "await client.getTasks(\n params: TasksQuery(\n uids: [5, 10, 13],\n ),\n);" +async_guide_filter_by_statuses_1: "await client.getTasks(\n params: TasksQuery(\n statuses: ['failed', 'canceled'],\n ),\n);" +async_guide_filter_by_types_1: "await client.getTasks(\n params: TasksQuery(\n types: ['dumpCreation', 'indexSwap'],\n ),\n);" async_guide_filter_by_index_uids_1: |- await client.getTasks(params: TasksQuery(indexUids: ['movies'])); delete_tasks_1: |- @@ -79,48 +29,27 @@ cancel_tasks_1: |- await client.cancelTasks(params: CancelTasksQuery(uids: [1, 2])); async_guide_canceled_by_1: |- await client.getTasks(params: TasksQuery(canceledBy: [9, 15])); -swap_indexes_1: |- - await client.swapIndexes([SwapIndex(['indexA', 'indexB']), SwapIndex(['indexX', 'indexY'])]); -search_parameter_guide_hitsperpage_1: |- - await client.index('movies').search('', SearchQuery(hitsPerPage: 15)) as PaginatedSearchResult; -search_parameter_guide_page_1: |- - await client.index('movies').search('', SearchQuery(page: 2)) as PaginatedSearchResult; -synonyms_guide_1: |- - await client.index('movies').updateSynonyms({ - 'great': ['fantastic'], - 'fantastic': ['great'] - }); -date_guide_index_1: |- - import 'dart:io'; - import 'dart:convert'; - - final json = await File('games.json').readAsString(); - - await client.index('games').addDocumentsJson(json); -date_guide_filterable_attributes_1: |- - await client.index('games').updateFilterableAttributes(['release_timestamp']); -date_guide_filter_1: |- - await client.index('games').search('', - SearchQuery(filter: - 'release_timestamp >= 1514761200 AND release_timestamp < 1672527600')); -date_guide_sortable_attributes_1: |- - await client.index('games').updateSortableAttributes(['release_timestamp']); -date_guide_sort_1: |- - await client.index('games').search('', SearchQuery(sort: ['release_timestamp:desc'])); +swap_indexes_1: "await client.swapIndexes([\n SwapIndex(['indexA', 'indexB']),\n SwapIndex(['indexX', 'indexY']),\n]);" +search_parameter_guide_hitsperpage_1: "await client\n .index('movies')\n .search('', SearchQuery(hitsPerPage: 15))\n .asPaginatedResult();" +search_parameter_guide_page_1: "await client\n .index('movies')\n .search('', SearchQuery(page: 2))\n .asPaginatedResult();" +synonyms_guide_1: "await client.index('movies').updateSynonyms({\n 'great': ['fantastic'],\n 'fantastic': ['great'],\n});" +date_guide_index_1: "\/\/import 'dart:io';\n\/\/import 'dart:convert';\nfinal json = await File('games.json').readAsString();\nawait client.index('games').addDocumentsJson(json);" +date_guide_filterable_attributes_1: "await client\n .index('games')\n .updateFilterableAttributes(['release_timestamp']);" +date_guide_filter_1: "await client.index('games').search(\n '',\n SearchQuery(\n filterExpression: Meili.and([\n Meili.gte(\n 'release_timestamp'.toMeiliAttribute(),\n Meili.value(DateTime(2017, 12, 31, 23, 0)),\n ),\n Meili.lt(\n 'release_timestamp'.toMeiliAttribute(),\n Meili.value(DateTime(2022, 12, 31, 23, 0)),\n ),\n ]),\n ),\n );" +date_guide_sortable_attributes_1: "await client\n .index('games')\n .updateSortableAttributes(['release_timestamp']);" +date_guide_sort_1: "await client\n .index('games')\n .search('', SearchQuery(sort: ['release_timestamp:desc']));" get_all_tasks_paginating_1: |- await client.getTasks(params: TasksQuery(limit: 2, from: 10)); get_all_tasks_paginating_2: |- await client.getTasks(params: TasksQuery(limit: 2, from: 8)); get_pagination_settings_1: |- await client.index('movies').getPagination(); -update_pagination_settings_1: |- - await client.index('books').updatePagination(Pagination(maxTotalHits: 100)); +update_pagination_settings_1: "await client\n .index('books')\n .updatePagination(Pagination(maxTotalHits: 100));" reset_pagination_settings_1: |- await client.index('movies').resetPagination(); get_faceting_settings_1: |- await client.index('movies').getFaceting(); -update_faceting_settings_1: |- - await client.index('books').updateFaceting(Faceting(maxValuesPerFacet: 2, sortFacetValuesBy: {'*': 'alpha', 'genres': 'count'})); +update_faceting_settings_1: "await client.index('books').updateFaceting(Faceting(\n maxValuesPerFacet: 2,\n sortFacetValuesBy: {\n '*': FacetingSortTypes.alpha,\n 'genres': FacetingSortTypes.count\n }));" reset_faceting_settings_1: |- await client.index('movies').resetFaceting(); get_one_index_1: |- @@ -133,8 +62,7 @@ update_an_index_1: |- await client.index('movies').update(primaryKey: 'id'); delete_an_index_1: |- await client.index('movies').delete(); -get_one_document_1: |- - await client.index('movies').getDocument(25684, fields: ['id', 'title', 'poster', 'release_date']); +get_one_document_1: "await client.index('movies').getDocument(25684,\n fields: ['id', 'title', 'poster', 'release_date']);" add_or_replace_documents_1: |- await client.index('movies').addDocuments([ { @@ -147,21 +75,12 @@ add_or_replace_documents_1: |- 'release_date': '2019-03-23' } ]); -add_or_update_documents_1: |- - await client.index('movies').updateDocuments([ - { - 'id': 287947, - 'title': 'Shazam ⚡️', - 'genres': 'comedy' - } - ]); +add_or_update_documents_1: "await client.index('movies').updateDocuments([\n {\n 'id': 287947,\n 'title': 'Shazam ⚡️',\n 'genres': 'comedy',\n }\n]);" delete_all_documents_1: |- await client.index('movies').deleteAllDocuments(); delete_one_document_1: |- await client.index('movies').deleteDocument(25684); -delete_documents_by_batch_1: |- - await client.index('movies') - .deleteDocuments(DeleteDocumentsQuery(ids: [23488, 153738, 437035, 363869])); +delete_documents_by_batch_1: "await client.index('movies').deleteDocuments(\n DeleteDocumentsQuery(\n ids: [23488, 153738, 437035, 363869],\n ),\n );" search_post_1: |- await client.index('movies').search('American ninja'); get_task_1: |- @@ -170,40 +89,12 @@ get_all_tasks_1: |- await client.getTasks(); get_settings_1: |- await client.index('movies').getSettings(); -update_settings_1: |- - await client.index('movies').updateSettings(IndexSettings( - rankingRules: [ - 'words', - 'typo', - 'proximity', - 'attribute', - 'sort', - 'exactness', - 'release_date:desc', - 'rank:desc' - ], - distinctAttribute: 'movie_id', - searchableAttributes: ['title', 'overview', 'genres'], - displayedAttributes: ['title', 'overview', 'genres', 'release_date'], - stopWords: ['the', 'a', 'an'], - sortableAttributes: ['title', 'release_date'], - synonyms: { - 'wolverine': ['xmen', 'logan'], - 'logan': ['wolverine'], - }, - )); +update_settings_1: "await client.index('movies').updateSettings(\n IndexSettings(\n rankingRules: [\n 'words',\n 'typo',\n 'proximity',\n 'attribute',\n 'sort',\n 'exactness',\n 'release_date:desc',\n 'rank:desc'\n ],\n distinctAttribute: 'movie_id',\n searchableAttributes: ['title', 'overview', 'genres'],\n displayedAttributes: [\n 'title',\n 'overview',\n 'genres',\n 'release_date'\n ],\n stopWords: ['the', 'a', 'an'],\n sortableAttributes: ['title', 'release_date'],\n synonyms: {\n 'wolverine': ['xmen', 'logan'],\n 'logan': ['wolverine'],\n },\n ),\n );" reset_settings_1: |- await client.index('movies').resetSettings(); get_synonyms_1: |- await client.index('movies').getSynonyms(); -update_synonyms_1: |- - await client.index('movies').updateSynonyms( - { - 'wolverine': ['xmen', 'logan'], - 'logan': ['wolverine', 'xmen'], - 'wow': ['world of warcraft'] - } - ); +update_synonyms_1: "await client.index('movies').updateSynonyms({\n 'wolverine': ['xmen', 'logan'],\n 'logan': ['wolverine', 'xmen'],\n 'wow': ['world of warcraft'],\n});" reset_synonyms_1: |- await client.index('movies').resetSynonyms(); get_stop_words_1: |- @@ -214,17 +105,7 @@ reset_stop_words_1: |- await client.index('movies').resetStopWords(); get_ranking_rules_1: |- await client.index('movies').getRankingRules(); -update_ranking_rules_1: |- - await client.index('movies').updateRankingRules([ - 'words', - 'typo', - 'proximity', - 'attribute', - 'sort', - 'exactness', - 'release_date:asc', - 'rank:desc' - ]); +update_ranking_rules_1: "await client.index('movies').updateRankingRules([\n 'words',\n 'typo',\n 'proximity',\n 'attribute',\n 'sort',\n 'exactness',\n 'release_date:asc',\n 'rank:desc',\n]);" reset_ranking_rules_1: |- await client.index('movies').resetRankingRules(); get_distinct_attribute_1: |- @@ -235,32 +116,17 @@ reset_distinct_attribute_1: |- await client.index('shoes').resetDistinctAttribute(); get_filterable_attributes_1: |- await client.index('movies').getFilterableAttributes(); -update_filterable_attributes_1: |- - await client.index('movies').updateFilterableAttributes([ - 'genres', - 'director' - ]); +update_filterable_attributes_1: "await client\n .index('movies')\n .updateFilterableAttributes(['genres', 'director']);" reset_filterable_attributes_1: |- await client.index('movies').resetFilterableAttributes(); get_searchable_attributes_1: |- await client.index('movies').getSearchableAttributes(); -update_searchable_attributes_1: |- - await client.index('movies').updateSearchableAttributes([ - 'title', - 'overview', - 'genres' - ]); +update_searchable_attributes_1: "await client\n .index('movies')\n .updateSearchableAttributes(['title', 'overview', 'genres']);" reset_searchable_attributes_1: |- await client.index('movies').resetSearchableAttributes(); get_displayed_attributes_1: |- await client.index('movies').getDisplayedAttributes(); -update_displayed_attributes_1: |- - await client.index('movies').updateDisplayedAttributes([ - 'title', - 'overview', - 'genres', - 'release_date' - ]); +update_displayed_attributes_1: "await client.index('movies').updateDisplayedAttributes([\n 'title',\n 'overview',\n 'genres',\n 'release_date',\n]);" reset_displayed_attributes_1: |- await client.index('movies').resetDisplayedAttributes(); get_typo_tolerance_1: |- @@ -286,54 +152,24 @@ get_version_1: |- await client.getVersion(); distinct_attribute_guide_1: |- await client.index('jackets').updateDistinctAttribute('product_id'); -field_properties_guide_searchable_1: |- - await client.index('movies').updateSearchableAttributes(['title', 'overview', 'genres']); -field_properties_guide_displayed_1: |- - await client.index('movies').updateDisplayedAttributes(['title', 'overview', 'genres', 'release_date']); -filtering_guide_1: |- - await client - .index('movie_ratings') - .search('Avengers', SearchQuery(filter: 'release_date > 795484800')); -filtering_guide_2: |- - await client.index('movie_ratings') - .search('Batman', - SearchQuery(filter: 'release_date > 795484800 AND (director = "Tim Burton" OR director = "Christopher Nolan")' - ) - ); -filtering_guide_3: |- - await client.index('movie_ratings').search('Planet of the Apes', - SearchQuery(filter: 'release_date > 1577884550 AND (NOT director = "Tim Burton")')); +field_properties_guide_searchable_1: "await client\n .index('movies')\n .updateSearchableAttributes(['title', 'overview', 'genres']);" +field_properties_guide_displayed_1: "await client.index('movies').updateDisplayedAttributes([\n 'title',\n 'overview',\n 'genres',\n 'release_date',\n]);" +filtering_guide_1: "await client.index('movie_ratings').search(\n 'Avengers',\n SearchQuery(\n filterExpression: Meili.gt(\n Meili.attr('release_date'),\n DateTime.utc(1995, 3, 18).toMeiliValue(),\n ),\n ),\n );" +filtering_guide_2: "await client.index('movie_ratings').search(\n 'Batman',\n SearchQuery(\n filterExpression: Meili.and([\n Meili.attr('release_date')\n .gt(DateTime.utc(1995, 3, 18).toMeiliValue()),\n Meili.or([\n 'director'.toMeiliAttribute().eq('Tim Burton'.toMeiliValue()),\n 'director'\n .toMeiliAttribute()\n .eq('Christopher Nolan'.toMeiliValue()),\n ]),\n ]),\n ),\n );" +filtering_guide_3: "await client.index('movie_ratings').search(\n 'Planet of the Apes',\n SearchQuery(\n filterExpression: Meili.and([\n Meili.attr('release_date')\n .gt(DateTime.utc(2020, 1, 1, 13, 15, 50).toMeiliValue()),\n Meili.not(\n Meili.attr('director').eq(\"Tim Burton\".toMeiliValue()),\n ),\n ]),\n ),\n );" search_parameter_guide_query_1: |- await client.index('movies').search('shifu'); search_parameter_guide_offset_1: |- await client.index('movies').search('shifu', SearchQuery(offset: 1)); search_parameter_guide_limit_1: |- await client.index('movies').search('shifu', SearchQuery(limit: 2)); -search_parameter_guide_matching_strategy_1: |- - await client.index('movies').search('big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.last)); -search_parameter_guide_matching_strategy_2: |- - await client.index('movies').search('big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.all)); -search_parameter_guide_retrieve_1: |- - await client - .index('movies') - .search('shifu', SearchQuery(attributesToRetrieve: ['overview', 'title'])); -search_parameter_guide_crop_1: |- - await client - .index('movies') - .search('shifu', SearchQuery(attributesToCrop: ['overview'], cropLength: 5)); -search_parameter_guide_highlight_1: |- - await client - .index('movies') - .search('winter feast', SearchQuery(attributesToHighlight: ['overview'])); -search_parameter_guide_show_matches_position_1: |- - await client.index('movies').search('winter feast', SearchQuery(showMatchesPosition: true)); -add_movies_json_1: |- - import 'dart:io'; - import 'dart:convert'; - - final json = await File('movies.json').readAsString(); - - await client.index('movies').addDocumentsJson(json); +search_parameter_guide_matching_strategy_1: "await client.index('movies').search(\n 'big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.last));" +search_parameter_guide_matching_strategy_2: "await client.index('movies').search(\n 'big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.all));" +search_parameter_guide_retrieve_1: "await client.index('movies').search(\n 'shifu', SearchQuery(attributesToRetrieve: ['overview', 'title']));" +search_parameter_guide_crop_1: "await client.index('movies').search(\n 'shifu', SearchQuery(attributesToCrop: ['overview'], cropLength: 5));" +search_parameter_guide_highlight_1: "await client.index('movies').search(\n 'winter feast', SearchQuery(attributesToHighlight: ['overview']));" +search_parameter_guide_show_matches_position_1: "await client\n .index('movies')\n .search('winter feast', SearchQuery(showMatchesPosition: true));" +add_movies_json_1: "\/\/ import 'dart:io';\n\/\/ import 'dart:convert';\nfinal json = await File('movies.json').readAsString();\nawait client.index('movies').addDocumentsJson(json);" primary_field_guide_create_index_primary_key: |- await client.createIndex('books', primaryKey: 'reference_number'); primary_field_guide_update_document_primary_key: |- @@ -369,30 +205,8 @@ typo_tolerance_guide_4: |- ), ); await client.index('movies').updateTypoTolerance(toUpdate); -getting_started_add_documents_md: |- - ```bash - dart pub add meilisearch - ``` - - ```dart - import 'package:meilisearch/meilisearch.dart'; - import 'dart:io'; - import 'dart:convert'; - - var client = MeiliSearchClient('http://localhost:7700', 'aSampleMasterKey'); - - final json = await File('movies.json').readAsString(); - - await client.index('movies').addDocumentsJson(json); - ``` - - [About this SDK](https://github.com/meilisearch/meilisearch-dart/) -getting_started_search_md: |- - ```dart - await client.index('movies').search('botman'); - ``` - - [About this SDK](https://github.com/meilisearch/meilisearch-dart/) +getting_started_add_documents_md: "```bash\ndart pub add meilisearch\n```\n```dart\nimport 'package:meilisearch\/meilisearch.dart';\nimport 'dart:io';\nimport 'dart:convert';\nvar client = MeiliSearchClient('http:\/\/localhost:7700', 'aSampleMasterKey');\nfinal json = await File('movies.json').readAsString();\nawait client.index('movies').addDocumentsJson(json);\n```\n[About this SDK](https:\/\/github.com\/meilisearch\/meilisearch-dart\/)" +getting_started_search_md: "```dart\nawait client.index('movies').search('botman');\n```\n[About this SDK](https:\/\/github.com\/meilisearch\/meilisearch-dart\/)" getting_started_update_ranking_rules: |- await client.index('movies').updateRankingRules([ 'exactness', @@ -404,10 +218,7 @@ getting_started_update_ranking_rules: |- 'release_date:asc', 'rank:desc' ]); -getting_started_update_searchable_attributes: |- - await client.index('movies').updateSearchableAttributes([ - 'title' - ]); +getting_started_update_searchable_attributes: "await client.index('movies').updateSearchableAttributes(['title']);" getting_started_update_stop_words: |- await client.index('movies').updateStopWords(['the']); getting_started_check_task_status: |- @@ -417,94 +228,49 @@ getting_started_synonyms: |- 'winnie': ['piglet'], 'piglet': ['winnie'] }); -getting_started_update_displayed_attributes: |- - await client.index('movies').updateDisplayedAttributes([ - 'title', - 'overview', - 'poster' - ]); -getting_started_add_meteorites: |- - final json = await File('meteorites.json').readAsString(); - - await client.index('meteorites').addDocumentsJson(json); -getting_started_configure_settings: |- - await client.index('meteorites').updateSettings(IndexSettings( - filterableAttributes: ['mass', '_geo'], - sortableAttributes: ['mass', '_geo'] - )); -getting_started_geo_radius: |- - await client.index('meteorites').search('', SearchQuery(filter: '_geoRadius(46.9480, 7.4474, 210000)')); -getting_started_geo_point: |- - await client.index('meteorites').search('', SearchQuery(sort: ['_geoPoint(48.8583701, 2.2922926):asc'])); -getting_started_sorting: |- - await client.index('meteorites').search('', SearchQuery(sort: ['mass:asc'], filter: 'mass < 200')); -getting_started_filtering: |- - await client.index('meteorites').search('', SearchQuery(filter: 'mass < 200')); -getting_started_faceting: |- - await client.index('books').updateFaceting(Faceting(maxValuesPerFacet: 2, sortFacetValuesBy: {'*': 'count'})); +getting_started_update_displayed_attributes: "await client\n .index('movies')\n .updateDisplayedAttributes(['title', 'overview', 'poster']);" +getting_started_add_meteorites: "final json = await File('meteorites.json').readAsString();\nawait client.index('meteorites').addDocumentsJson(json);" +getting_started_configure_settings: "await client.index('meteorites').updateSettings(IndexSettings(\n filterableAttributes: ['mass', '_geo'],\n sortableAttributes: ['mass', '_geo']));" +getting_started_geo_radius: "await client.index('meteorites').search(\n '',\n SearchQuery(\n filterExpression: Meili.geoRadius(\n (lat: 46.9480, lng: 7.4474),\n 210000,\n ),\n ),\n );" +getting_started_geo_point: "await client.index('meteorites').search(\n '', SearchQuery(sort: ['_geoPoint(48.8583701, 2.2922926):asc']));" +getting_started_sorting: "await client.index('meteorites').search(\n '',\n SearchQuery(\n sort: ['mass:asc'],\n filterExpression: Meili.attr('mass').lt(200.toMeiliValue()),\n ),\n );" +getting_started_filtering: "await client\n .index('meteorites')\n .search('', SearchQuery(filter: 'mass < 200'));" +getting_started_faceting: "await client.index('books').updateFaceting(Faceting(\n maxValuesPerFacet: 2,\n sortFacetValuesBy: {'*': FacetingSortTypes.count}));" getting_started_typo_tolerance: |- final toUpdate = TypoTolerance( minWordSizeForTypos: MinWordSizeForTypos(oneTypo: 4), ); await client.index('movies').updateTypoTolerance(toUpdate); -getting_started_pagination: |- - await client.index('books').updatePagination(Pagination(maxTotalHits: 500)); +getting_started_pagination: "await client\n .index('books')\n .updatePagination(Pagination(maxTotalHits: 500));" filtering_update_settings_1: |- await client.index('movies').updateFilterableAttributes([ 'director', 'genres', ]); -faceted_search_walkthrough_filter_1: |- - await client.index('movies').search('thriller', SearchQuery(filter: [ - ['genres = Horror', 'genres = Mystery'], - 'director = "Jordan Peele"' - ])); +faceted_search_walkthrough_filter_1: "await client.index('movies').search(\n 'thriller',\n SearchQuery(filter: [\n ['genres = Horror', 'genres = Mystery'],\n 'director = \"Jordan Peele\"'\n ]));" post_dump_1: |- await client.createDump(); phrase_search_1: |- await client.index('movies').search('"african american" horror'); sorting_guide_update_sortable_attributes_1: |- await client.index('books').updateSortableAttributes(['author', 'price']); -sorting_guide_update_ranking_rules_1: |- - await client.index('books').updateRankingRules([ - 'words', - 'sort', - 'typo', - 'proximity', - 'attribute', - 'exactness' - ]); -sorting_guide_sort_parameter_1: |- - await client.index('books').search('science fiction', SearchQuery(sort: ['price:asc'])); -sorting_guide_sort_parameter_2: |- - await client.index('books').search('butler', SearchQuery(sort: ['author:desc'])); +sorting_guide_update_ranking_rules_1: "await client.index('books').updateRankingRules(\n ['words', 'sort', 'typo', 'proximity', 'attribute', 'exactness']);" +sorting_guide_sort_parameter_1: "await client\n .index('books')\n .search('science fiction', SearchQuery(sort: ['price:asc']));" +sorting_guide_sort_parameter_2: "await client\n .index('books')\n .search('butler', SearchQuery(sort: ['author:desc']));" get_sortable_attributes_1: |- await client.index('books').getSortableAttributes(); -update_sortable_attributes_1: |- - await client.index('books').updateSortableAttributes([ - 'price', - 'author' - ]); +update_sortable_attributes_1: "await client.index('books').updateSortableAttributes(['price', 'author']);" reset_sortable_attributes_1: |- await client.index('books').resetSortableAttributes(); -search_parameter_guide_sort_1: |- - await client.index('books').search('science fiction', SearchQuery(sort: ['price:asc'])); +search_parameter_guide_sort_1: "await client\n .index('books')\n .search('science fiction', SearchQuery(sort: ['price:asc']));" geosearch_guide_filter_settings_1: |- await client.index('restaurants').updateFilterableAttributes(['_geo']); -geosearch_guide_filter_usage_1: |- - await await client - .index('restaurants') - .search('', SearchQuery(filter: '_geoRadius(45.472735, 9.184019, 2000)')); -geosearch_guide_filter_usage_2: |- - await await client - .index('restaurants') - .search('', SearchQuery(filter: '_geoRadius(45.472735, 9.184019, 2000) AND type = pizza')); +geosearch_guide_filter_usage_1: "await client.index('restaurants').search(\n '',\n SearchQuery(\n filterExpression: Meili.geoRadius(\n (lat: 45.472735, lng: 9.184019),\n 2000,\n ),\n ),\n );" +geosearch_guide_filter_usage_2: "await client.index('restaurants').search(\n '',\n SearchQuery(\n filterExpression: Meili.and([\n Meili.geoRadius(\n (lat: 45.472735, lng: 9.184019),\n 2000,\n ),\n Meili.attr('type').eq('pizza'.toMeiliValue())\n ]),\n ),\n );" geosearch_guide_sort_settings_1: |- await client.index('restaurants').updateSortableAttributes(['_geo']); -geosearch_guide_sort_usage_1: |- - await client.index('restaurants').search('', SearchQuery(sort: ['_geoPoint(48.8561446, 2.2978204):asc'])); -geosearch_guide_sort_usage_2: |- - await client.index('restaurants').search('', SearchQuery(sort: ['_geoPoint(48.8561446, 2.2978204):asc', 'rating:desc'])); +geosearch_guide_sort_usage_1: "await client.index('restaurants').search(\n '', SearchQuery(sort: ['_geoPoint(48.8561446, 2.2978204):asc']));" +geosearch_guide_sort_usage_2: "await client.index('restaurants').search(\n '',\n SearchQuery(\n sort: ['_geoPoint(48.8561446, 2.2978204):asc', 'rating:desc']));" authorization_header_1: |- var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); await client.getKeys(); @@ -512,27 +278,14 @@ get_one_key_1: |- await client.getKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d'); get_all_keys_1: |- await client.getKeys(params: KeysQuery(limit: 3)); -create_a_key_1: |- - await client.createKey( - description: 'Add documents: Products API key', - actions: ['documents.add'], - indexes: ['products'], - expiresAt: DateTime(2042, 04, 02) - ); -update_a_key_1: |- - await client.updateKey( - '6062abda-a5aa-4414-ac91-ecd7944c0f8d', - description: 'Manage documents: Products/Reviews API key', - name: 'Products/Reviews API key' - ); +create_a_key_1: "await client.createKey(\n description: 'Add documents: Products API key',\n actions: ['documents.add'],\n indexes: ['products'],\n expiresAt: DateTime(2042, 04, 02));" +update_a_key_1: "await client.updateKey(\n '6062abda-a5aa-4414-ac91-ecd7944c0f8d',\n description: 'Manage documents: Products\/Reviews API key',\n name: 'Products\/Reviews API key',\n);" delete_a_key_1: |- await client.deleteKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d'); security_guide_search_key_1: |- var client = MeiliSearchClient('http://localhost:7700', 'apiKey'); await client.index('patient_medical_records').search(''); -security_guide_update_key_1: |- - var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); - await client.updateKey('74c9c733-3368-4738-bbe5-1d18a5fecb37', description: 'Default Search API Key'); +security_guide_update_key_1: "var client = MeiliSearchClient('http:\/\/localhost:7700', 'masterKey');\nawait client.updateKey(\n '74c9c733-3368-4738-bbe5-1d18a5fecb37',\n description: 'Default Search API Key',\n);" security_guide_create_key_1: |- var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); await client.createKey( @@ -547,27 +300,7 @@ security_guide_list_keys_1: |- security_guide_delete_key_1: |- var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); await client.deleteKey('ac5cd97d-5a4b-4226-a868-2d0eb6d197ab'); -search_parameter_guide_crop_marker_1: |- - await client - .index('movies') - .search('shifu', SearchQuery(attributesToCrop: ['overview'], cropMarker: '[…]')); -search_parameter_guide_highlight_tag_1: |- - await client.index('movies').search( - 'winter feast', - SearchQuery( - attributesToHighlight: ['overview'], - highlightPreTag: '', - highlightPostTag: '')); -geosearch_guide_filter_usage_3: |- - await client.index('restaurants') - .search('', SearchQuery(filter: '_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])')), - }); -search_get_1: "curl \\\n -X GET 'http:\/\/localhost:7700\/indexes\/movies\/search?q=american%20ninja'" -tenant_token_guide_search_no_sdk_1: "curl \\\n -X POST 'http:\/\/localhost:7700\/indexes\/patient_medical_records\/search' \\\n -H 'Authorization: Bearer TENANT_TOKEN'" -updating_guide_check_version_new_authorization_header: "curl \\\n -X GET 'http:\/\/\/version' \\\n -H 'Authorization: Bearer API_KEY'" -updating_guide_check_version_old_authorization_header: "curl \\\n -X GET 'http:\/\/\/version' \\\n -H 'X-Meili-API-Key: API_KEY'" -updating_guide_get_displayed_attributes_old_authorization_header: "# whenever you see {index_uid}, replace it with your index's unique id\ncurl \\\n -X GET 'http:\/\/\/indexes\/{index_uid}\/settings\/displayed-attributes' \\\n -H 'X-Meili-API-Key: API_KEY'" -updating_guide_reset_displayed_attributes_old_authorization_header: "curl \\\n -X DELETE 'http:\/\/\/indexes\/{index_uid}\/settings\/displayed-attributes' \\\n -H 'X-Meili-API-Key: API_KEY'" -updating_guide_create_dump: "curl \\\n -X POST 'http:\/\/\/dumps' \\\n -H 'Authorization: Bearer API_KEY'\n# -H 'X-Meili-API-Key: API_KEY' for v0.24 or below" -get_experimental_features_1: "curl \\\n -X GET 'http:\/\/localhost:7700\/experimental-features\/'" -update_experimental_features_1: "curl \\\n -X PATCH 'http:\/\/localhost:7700\/experimental-features\/' \\\n -H 'Content-Type: application\/json' \\\n --data-binary '{\n \"scoreDetails\": true\n }'" +search_parameter_guide_crop_marker_1: "await client.index('movies').search(\n 'shifu',\n SearchQuery(\n attributesToCrop: ['overview'],\n cropMarker: '[…]',\n ),\n );" +search_parameter_guide_highlight_tag_1: "await client.index('movies').search(\n 'winter feast',\n SearchQuery(\n attributesToHighlight: ['overview'],\n highlightPreTag: '',\n highlightPostTag: '<\/span>',\n ),\n );" +geosearch_guide_filter_usage_3: "await client.index('restaurants').search(\n '',\n SearchQuery(\n filter:\n '_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])'));\n});" +search_get_1: await client.index('movies').search('American ninja'); diff --git a/test/code_samples.dart b/test/code_samples.dart index 96677f6..3de892b 100644 --- a/test/code_samples.dart +++ b/test/code_samples.dart @@ -1,7 +1,10 @@ +// ignore_for_file: unused_element + +import 'dart:io'; + import 'package:meilisearch/meilisearch.dart'; import 'package:test/test.dart'; -import 'utils/books.dart'; import 'utils/client.dart'; void main() { @@ -9,16 +12,877 @@ void main() { // .code-samples.meilisearch.yaml // it's subject to tests, lint rules, deprecation notices, etc... group('code samples', () { + setUpClient(); + test('excerpts', () async { - // #docregion search_parameter_guide_show_ranking_score_1 + void a1() async { + // #docregion typo_tolerance_guide_1 + final toUpdate = TypoTolerance(enabled: false); + await client.index('movies').updateTypoTolerance(toUpdate); + // #enddocregion + } + + void a2() async { + // #docregion typo_tolerance_guide_2 + final toUpdate = TypoTolerance( + disableOnAttributes: ['title'], + ); + await client.index('movies').updateTypoTolerance(toUpdate); + // #enddocregion + } + + void a3() async { + // #docregion typo_tolerance_guide_3 + final toUpdate = TypoTolerance( + disableOnWords: ['shrek'], + ); + await client.index('movies').updateTypoTolerance(toUpdate); + // #enddocregion + } + + void a4() async { + // #docregion typo_tolerance_guide_4 + final toUpdate = TypoTolerance( + minWordSizeForTypos: MinWordSizeForTypos( + oneTypo: 4, + twoTypos: 10, + ), + ); + await client.index('movies').updateTypoTolerance(toUpdate); + // #enddocregion + } + + void a8() async { + // #docregion getting_started_add_meteorites + final json = await File('meteorites.json').readAsString(); + + await client.index('meteorites').addDocumentsJson(json); + // #enddocregion + } + + void a10() async { + // #docregion add_movies_json_1 + // import 'dart:io'; + // import 'dart:convert'; + final json = await File('movies.json').readAsString(); + await client.index('movies').addDocumentsJson(json); + // #enddocregion + } + + void a11() async { + // #docregion security_guide_delete_key_1 + var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); + await client.deleteKey('ac5cd97d-5a4b-4226-a868-2d0eb6d197ab'); + // #enddocregion + } + + void a12() async { + // #docregion security_guide_list_keys_1 + var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); + await client.getKeys(); + // #enddocregion + } + + void a13() async { + // #docregion security_guide_create_key_1 + var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); + await client.createKey( + description: 'Search patient records key', + actions: ['search'], + indexes: ['patient_medical_records'], + expiresAt: DateTime(2023, 01, 01), + ); + // #enddocregion + } + + void a14() async { + // #docregion authorization_header_1 + var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); + await client.getKeys(); + // #enddocregion + } + + void a15() async { + // #docregion security_guide_search_key_1 + var client = MeiliSearchClient('http://localhost:7700', 'apiKey'); + await client.index('patient_medical_records').search(''); + // #enddocregion + } + + void a16() async { + // #docregion security_guide_update_key_1 + var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); + await client.updateKey( + '74c9c733-3368-4738-bbe5-1d18a5fecb37', + description: 'Default Search API Key', + ); + // #enddocregion + } + + // #docregion date_guide_index_1 + //import 'dart:io'; + //import 'dart:convert'; + + final json = await File('games.json').readAsString(); + + await client.index('games').addDocumentsJson(json); + // #enddocregion + + // #docregion date_guide_filterable_attributes_1 + await client + .index('games') + .updateFilterableAttributes(['release_timestamp']); + // #enddocregion + + // #docregion date_guide_filter_1 + await client.index('games').search( + '', + SearchQuery( + filterExpression: Meili.and([ + Meili.gte( + 'release_timestamp'.toMeiliAttribute(), + Meili.value(DateTime(2017, 12, 31, 23, 0)), + ), + Meili.lt( + 'release_timestamp'.toMeiliAttribute(), + Meili.value(DateTime(2022, 12, 31, 23, 0)), + ), + ]), + ), + ); + // #enddocregion + + // #docregion date_guide_sortable_attributes_1 + await client + .index('games') + .updateSortableAttributes(['release_timestamp']); + // #enddocregion + + // #docregion date_guide_sort_1 + await client + .index('games') + .search('', SearchQuery(sort: ['release_timestamp:desc'])); + // #enddocregion + + // #docregion get_all_tasks_paginating_1 + await client.getTasks(params: TasksQuery(limit: 2, from: 10)); + // #enddocregion + + // #docregion get_all_tasks_paginating_2 + await client.getTasks(params: TasksQuery(limit: 2, from: 8)); + // #enddocregion + + // #docregion get_pagination_settings_1 + await client.index('movies').getPagination(); + // #enddocregion + + // #docregion update_pagination_settings_1 + await client + .index('books') + .updatePagination(Pagination(maxTotalHits: 100)); + // #enddocregion + + // #docregion reset_pagination_settings_1 + await client.index('movies').resetPagination(); + // #enddocregion + + // #docregion get_faceting_settings_1 + await client.index('movies').getFaceting(); + // #enddocregion + + // #docregion update_faceting_settings_1 + await client.index('books').updateFaceting(Faceting( + maxValuesPerFacet: 2, + sortFacetValuesBy: { + '*': FacetingSortTypes.alpha, + 'genres': FacetingSortTypes.count + })); + // #enddocregion + + // #docregion reset_faceting_settings_1 + await client.index('movies').resetFaceting(); + // #enddocregion + + // #docregion get_one_index_1 + await client.getIndex('movies'); + // #enddocregion + + // #docregion list_all_indexes_1 + await client.getIndexes(params: IndexesQuery(limit: 3)); + // #enddocregion + + // #docregion create_an_index_1 + await client.createIndex('movies', primaryKey: 'id'); + // #enddocregion + + // #docregion update_an_index_1 + await client.index('movies').update(primaryKey: 'id'); + // #enddocregion + + // #docregion delete_an_index_1 + await client.index('movies').delete(); + // #enddocregion + + // #docregion get_one_document_1 + await client.index('movies').getDocument(25684, + fields: ['id', 'title', 'poster', 'release_date']); + // #enddocregion + + // #docregion add_or_replace_documents_1 + await client.index('movies').addDocuments([ + { + 'id': 287947, + 'title': 'Shazam', + 'poster': + 'https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg', + 'overview': + 'A boy is given the ability to become an adult superhero in times of need with a single magic word.', + 'release_date': '2019-03-23' + } + ]); + // #enddocregion + + // #docregion add_or_update_documents_1 + await client.index('movies').updateDocuments([ + { + 'id': 287947, + 'title': 'Shazam ⚡️', + 'genres': 'comedy', + } + ]); + // #enddocregion + + // #docregion delete_all_documents_1 + await client.index('movies').deleteAllDocuments(); + // #enddocregion + + // #docregion delete_one_document_1 + await client.index('movies').deleteDocument(25684); + // #enddocregion + + // #docregion delete_documents_by_batch_1 + await client.index('movies').deleteDocuments( + DeleteDocumentsQuery( + ids: [23488, 153738, 437035, 363869], + ), + ); + // #enddocregion + + // #docregion search_post_1 + await client.index('movies').search('American ninja'); + // #enddocregion + + // #docregion get_task_1 + await client.getTask(1); + // #enddocregion + + // #docregion get_all_tasks_1 + await client.getTasks(); + // #enddocregion + + // #docregion get_settings_1 + await client.index('movies').getSettings(); + // #enddocregion + + // #docregion update_settings_1 + await client.index('movies').updateSettings( + IndexSettings( + rankingRules: [ + 'words', + 'typo', + 'proximity', + 'attribute', + 'sort', + 'exactness', + 'release_date:desc', + 'rank:desc' + ], + distinctAttribute: 'movie_id', + searchableAttributes: ['title', 'overview', 'genres'], + displayedAttributes: [ + 'title', + 'overview', + 'genres', + 'release_date' + ], + stopWords: ['the', 'a', 'an'], + sortableAttributes: ['title', 'release_date'], + synonyms: { + 'wolverine': ['xmen', 'logan'], + 'logan': ['wolverine'], + }, + ), + ); + // #enddocregion + + // #docregion reset_settings_1 + await client.index('movies').resetSettings(); + // #enddocregion + + // #docregion get_synonyms_1 + await client.index('movies').getSynonyms(); + // #enddocregion + + // #docregion update_synonyms_1 + await client.index('movies').updateSynonyms({ + 'wolverine': ['xmen', 'logan'], + 'logan': ['wolverine', 'xmen'], + 'wow': ['world of warcraft'], + }); + // #enddocregion + + // #docregion reset_synonyms_1 + await client.index('movies').resetSynonyms(); + // #enddocregion + + // #docregion get_stop_words_1 + await client.index('movies').getStopWords(); + // #enddocregion + + // #docregion update_stop_words_1 + await client.index('movies').updateStopWords(['of', 'the', 'to']); + // #enddocregion + + // #docregion reset_stop_words_1 + await client.index('movies').resetStopWords(); + // #enddocregion + + // #docregion get_ranking_rules_1 + await client.index('movies').getRankingRules(); + // #enddocregion + + // #docregion update_ranking_rules_1 + await client.index('movies').updateRankingRules([ + 'words', + 'typo', + 'proximity', + 'attribute', + 'sort', + 'exactness', + 'release_date:asc', + 'rank:desc', + ]); + // #enddocregion + + // #docregion reset_ranking_rules_1 + await client.index('movies').resetRankingRules(); + // #enddocregion + + // #docregion get_distinct_attribute_1 + await client.index('shoes').getDistinctAttribute(); + // #enddocregion + + // #docregion update_distinct_attribute_1 + await client.index('shoes').updateDistinctAttribute('skuid'); + // #enddocregion + + // #docregion reset_distinct_attribute_1 + await client.index('shoes').resetDistinctAttribute(); + // #enddocregion + + // #docregion get_filterable_attributes_1 + await client.index('movies').getFilterableAttributes(); + // #enddocregion + + // #docregion update_filterable_attributes_1 await client .index('movies') - .search('dragon', SearchQuery(showRankingScore: true)); + .updateFilterableAttributes(['genres', 'director']); + // #enddocregion + + // #docregion reset_filterable_attributes_1 + await client.index('movies').resetFilterableAttributes(); // #enddocregion - // #docregion search_get_1 - await client.index('movies').search('american ninja'); + // #docregion get_searchable_attributes_1 + await client.index('movies').getSearchableAttributes(); // #enddocregion + + // #docregion update_searchable_attributes_1 + await client + .index('movies') + .updateSearchableAttributes(['title', 'overview', 'genres']); + // #enddocregion + + // #docregion reset_searchable_attributes_1 + await client.index('movies').resetSearchableAttributes(); + // #enddocregion + + // #docregion get_displayed_attributes_1 + await client.index('movies').getDisplayedAttributes(); + // #enddocregion + + // #docregion update_displayed_attributes_1 + await client.index('movies').updateDisplayedAttributes([ + 'title', + 'overview', + 'genres', + 'release_date', + ]); + // #enddocregion + + // #docregion reset_displayed_attributes_1 + await client.index('movies').resetDisplayedAttributes(); + // #enddocregion + + // #docregion get_typo_tolerance_1 + await client.index('books').getTypoTolerance(); + // #enddocregion + + // #docregion update_typo_tolerance_1 + final toUpdate = TypoTolerance( + minWordSizeForTypos: MinWordSizeForTypos( + oneTypo: 4, + twoTypos: 10, + ), + disableOnAttributes: ['title'], + ); + await client.index('books').updateTypoTolerance(toUpdate); + // #enddocregion + + // #docregion reset_typo_tolerance_1 + await client.index('books').resetTypoTolerance(); + // #enddocregion + + // #docregion get_index_stats_1 + await client.index('movies').getStats(); + // #enddocregion + + // #docregion get_indexes_stats_1 + await client.getStats(); + // #enddocregion + + // #docregion get_health_1 + await client.health(); + // #enddocregion + + // #docregion get_version_1 + await client.getVersion(); + // #enddocregion + + // #docregion distinct_attribute_guide_1 + await client.index('jackets').updateDistinctAttribute('product_id'); + // #enddocregion + + // #docregion field_properties_guide_searchable_1 + await client + .index('movies') + .updateSearchableAttributes(['title', 'overview', 'genres']); + // #enddocregion + + // #docregion field_properties_guide_displayed_1 + await client.index('movies').updateDisplayedAttributes([ + 'title', + 'overview', + 'genres', + 'release_date', + ]); + // #enddocregion + + // #docregion filtering_guide_1 + await client.index('movie_ratings').search( + 'Avengers', + SearchQuery( + filterExpression: Meili.gt( + Meili.attr('release_date'), + DateTime.utc(1995, 3, 18).toMeiliValue(), + ), + ), + ); + // #enddocregion + + // #docregion filtering_guide_2 + await client.index('movie_ratings').search( + 'Batman', + SearchQuery( + filterExpression: Meili.and([ + Meili.attr('release_date') + .gt(DateTime.utc(1995, 3, 18).toMeiliValue()), + Meili.or([ + 'director'.toMeiliAttribute().eq('Tim Burton'.toMeiliValue()), + 'director' + .toMeiliAttribute() + .eq('Christopher Nolan'.toMeiliValue()), + ]), + ]), + ), + ); + // #enddocregion + + // #docregion filtering_guide_3 + await client.index('movie_ratings').search( + 'Planet of the Apes', + SearchQuery( + filterExpression: Meili.and([ + Meili.attr('release_date') + .gt(DateTime.utc(2020, 1, 1, 13, 15, 50).toMeiliValue()), + Meili.not( + Meili.attr('director').eq("Tim Burton".toMeiliValue()), + ), + ]), + ), + ); + // #enddocregion + + // #docregion search_parameter_guide_query_1 + await client.index('movies').search('shifu'); + // #enddocregion + + // #docregion search_parameter_guide_offset_1 + await client.index('movies').search('shifu', SearchQuery(offset: 1)); + // #enddocregion + + // #docregion search_parameter_guide_limit_1 + await client.index('movies').search('shifu', SearchQuery(limit: 2)); + // #enddocregion + + // #docregion search_parameter_guide_matching_strategy_1 + await client.index('movies').search( + 'big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.last)); + // #enddocregion + + // #docregion search_parameter_guide_matching_strategy_2 + await client.index('movies').search( + 'big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.all)); + // #enddocregion + + // #docregion search_parameter_guide_retrieve_1 + await client.index('movies').search( + 'shifu', SearchQuery(attributesToRetrieve: ['overview', 'title'])); + // #enddocregion + + // #docregion search_parameter_guide_crop_1 + await client.index('movies').search( + 'shifu', SearchQuery(attributesToCrop: ['overview'], cropLength: 5)); + // #enddocregion + + // #docregion search_parameter_guide_highlight_1 + await client.index('movies').search( + 'winter feast', SearchQuery(attributesToHighlight: ['overview'])); + // #enddocregion + + // #docregion search_parameter_guide_show_matches_position_1 + await client + .index('movies') + .search('winter feast', SearchQuery(showMatchesPosition: true)); + // #enddocregion + + // #docregion primary_field_guide_create_index_primary_key + await client.createIndex('books', primaryKey: 'reference_number'); + // #enddocregion + + // #docregion primary_field_guide_update_document_primary_key + await client.updateIndex('books', 'title'); + // #enddocregion + + // #docregion primary_field_guide_add_document_primary_key + await client.index('movies').addDocuments([ + { + 'reference_number': 287947, + 'title': 'Diary of a Wimpy Kid', + 'author': 'Jeff Kinney', + 'genres': ['comedy', 'humor'], + 'price': 5.00 + } + ], primaryKey: 'reference_number'); + // #enddocregion + + // #docregion getting_started_update_ranking_rules + await client.index('movies').updateRankingRules([ + 'exactness', + 'words', + 'typo', + 'proximity', + 'attribute', + 'sort', + 'release_date:asc', + 'rank:desc', + ]); + // #enddocregion + + // #docregion getting_started_update_searchable_attributes + await client.index('movies').updateSearchableAttributes(['title']); + // #enddocregion + + // #docregion getting_started_update_stop_words + await client.index('movies').updateStopWords(['the']); + // #enddocregion + + // #docregion getting_started_check_task_status + await client.getTask(0); + // #enddocregion + + // #docregion getting_started_synonyms + await client.index('movies').updateSynonyms({ + 'winnie': ['piglet'], + 'piglet': ['winnie'], + }); + // #enddocregion + + // #docregion getting_started_update_displayed_attributes + await client + .index('movies') + .updateDisplayedAttributes(['title', 'overview', 'poster']); + // #enddocregion + + // #docregion getting_started_configure_settings + await client.index('meteorites').updateSettings(IndexSettings( + filterableAttributes: ['mass', '_geo'], + sortableAttributes: ['mass', '_geo'])); + // #enddocregion + + // #docregion getting_started_geo_radius + await client.index('meteorites').search( + '', + SearchQuery( + filterExpression: Meili.geoRadius( + (lat: 46.9480, lng: 7.4474), + 210000, + ), + ), + ); + // #enddocregion + + // #docregion getting_started_geo_point + await client.index('meteorites').search( + '', SearchQuery(sort: ['_geoPoint(48.8583701, 2.2922926):asc'])); + // #enddocregion + + // #docregion getting_started_sorting + await client.index('meteorites').search( + '', + SearchQuery( + sort: ['mass:asc'], + filterExpression: Meili.attr('mass').lt(200.toMeiliValue()), + ), + ); + // #enddocregion + + // #docregion getting_started_filtering + await client + .index('meteorites') + .search('', SearchQuery(filter: 'mass < 200')); + // #enddocregion + + // #docregion getting_started_faceting + await client.index('books').updateFaceting(Faceting( + maxValuesPerFacet: 2, + sortFacetValuesBy: {'*': FacetingSortTypes.count})); + // #enddocregion + + void a9() async { + // #docregion getting_started_typo_tolerance + final toUpdate = TypoTolerance( + minWordSizeForTypos: MinWordSizeForTypos(oneTypo: 4), + ); + await client.index('movies').updateTypoTolerance(toUpdate); + // #enddocregion + } + + // #docregion getting_started_pagination + await client + .index('books') + .updatePagination(Pagination(maxTotalHits: 500)); + // #enddocregion + + // #docregion filtering_update_settings_1 + await client.index('movies').updateFilterableAttributes([ + 'director', + 'genres', + ]); + // #enddocregion + + // #docregion faceted_search_walkthrough_filter_1 + await client.index('movies').search( + 'thriller', + SearchQuery(filter: [ + ['genres = Horror', 'genres = Mystery'], + 'director = "Jordan Peele"' + ])); + // #enddocregion + + // #docregion post_dump_1 + await client.createDump(); + // #enddocregion + + // #docregion phrase_search_1 + await client.index('movies').search('"african american" horror'); + // #enddocregion + + // #docregion sorting_guide_update_sortable_attributes_1 + await client.index('books').updateSortableAttributes(['author', 'price']); + // #enddocregion + + // #docregion sorting_guide_update_ranking_rules_1 + await client.index('books').updateRankingRules( + ['words', 'sort', 'typo', 'proximity', 'attribute', 'exactness']); + // #enddocregion + + // #docregion sorting_guide_sort_parameter_1 + await client + .index('books') + .search('science fiction', SearchQuery(sort: ['price:asc'])); + // #enddocregion + + // #docregion sorting_guide_sort_parameter_2 + await client + .index('books') + .search('butler', SearchQuery(sort: ['author:desc'])); + // #enddocregion + + // #docregion get_sortable_attributes_1 + await client.index('books').getSortableAttributes(); + // #enddocregion + + // #docregion update_sortable_attributes_1 + await client.index('books').updateSortableAttributes(['price', 'author']); + // #enddocregion + + // #docregion reset_sortable_attributes_1 + await client.index('books').resetSortableAttributes(); + // #enddocregion + + // #docregion search_parameter_guide_sort_1 + await client + .index('books') + .search('science fiction', SearchQuery(sort: ['price:asc'])); + // #enddocregion + + // #docregion geosearch_guide_filter_settings_1 + await client.index('restaurants').updateFilterableAttributes(['_geo']); + // #enddocregion + + // #docregion geosearch_guide_filter_usage_1 + await client.index('restaurants').search( + '', + SearchQuery( + filterExpression: Meili.geoRadius( + (lat: 45.472735, lng: 9.184019), + 2000, + ), + ), + ); + // #enddocregion + + // #docregion geosearch_guide_filter_usage_2 + await client.index('restaurants').search( + '', + SearchQuery( + filterExpression: Meili.and([ + Meili.geoRadius( + (lat: 45.472735, lng: 9.184019), + 2000, + ), + Meili.attr('type').eq('pizza'.toMeiliValue()) + ]), + ), + ); + // #enddocregion + + // #docregion geosearch_guide_sort_settings_1 + await client.index('restaurants').updateSortableAttributes(['_geo']); + // #enddocregion + + // #docregion geosearch_guide_sort_usage_1 + await client.index('restaurants').search( + '', SearchQuery(sort: ['_geoPoint(48.8561446, 2.2978204):asc'])); + // #enddocregion + + // #docregion geosearch_guide_sort_usage_2 + await client.index('restaurants').search( + '', + SearchQuery( + sort: ['_geoPoint(48.8561446, 2.2978204):asc', 'rating:desc'])); + // #enddocregion + + // #docregion get_one_key_1 + await client.getKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d'); + // #enddocregion + + // #docregion get_all_keys_1 + await client.getKeys(params: KeysQuery(limit: 3)); + // #enddocregion + + // #docregion create_a_key_1 + await client.createKey( + description: 'Add documents: Products API key', + actions: ['documents.add'], + indexes: ['products'], + expiresAt: DateTime(2042, 04, 02)); + // #enddocregion + + // #docregion update_a_key_1 + await client.updateKey( + '6062abda-a5aa-4414-ac91-ecd7944c0f8d', + description: 'Manage documents: Products/Reviews API key', + name: 'Products/Reviews API key', + ); + // #enddocregion + + // #docregion delete_a_key_1 + await client.deleteKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d'); + // #enddocregion + + // #docregion search_parameter_guide_crop_marker_1 + await client.index('movies').search( + 'shifu', + SearchQuery( + attributesToCrop: ['overview'], + cropMarker: '[…]', + ), + ); + // #enddocregion + + // #docregion search_parameter_guide_highlight_tag_1 + await client.index('movies').search( + 'winter feast', + SearchQuery( + attributesToHighlight: ['overview'], + highlightPreTag: '', + highlightPostTag: '', + ), + ); + // #enddocregion + + // #docregion geosearch_guide_filter_usage_3 + await client.index('restaurants').search( + '', + SearchQuery( + filter: + '_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])')); }); + // #enddocregion + // skip this test, since it's only used for generating code samples }, skip: true); + +// unformatted examples +/* + // #docregion getting_started_search_md + ```dart + await client.index('movies').search('botman'); + ``` + + [About this SDK](https://github.com/meilisearch/meilisearch-dart/) + // #enddocregion + + // #docregion getting_started_add_documents_md + ```bash + dart pub add meilisearch + ``` + + ```dart + import 'package:meilisearch/meilisearch.dart'; + import 'dart:io'; + import 'dart:convert'; + + var client = MeiliSearchClient('http://localhost:7700', 'aSampleMasterKey'); + + final json = await File('movies.json').readAsString(); + + await client.index('movies').addDocumentsJson(json); + ``` + + [About this SDK](https://github.com/meilisearch/meilisearch-dart/) + // #enddocregion +*/ } diff --git a/test/documents_test.dart b/test/documents_test.dart index b0c7a8e..8023a8f 100644 --- a/test/documents_test.dart +++ b/test/documents_test.dart @@ -557,4 +557,49 @@ void main() { }); }); }); + + test( + 'documents code samples', + () async { + // #docregion delete_documents_by_filter_1 + await client.index('movies').deleteDocuments( + DeleteDocumentsQuery( + filterExpression: Meili.or([ + Meili.attr('genres').eq(Meili.value('action')), + Meili.attr('genres').eq(Meili.value('adventure')), + ]), + ), + ); + // #enddocregion + + // #docregion get_documents_1 + await client.index('movies').getDocuments( + params: DocumentsQuery( + limit: 2, + filter: Meili.attr('genres').eq('action'.toMeiliValue()), + ), + ); + // #enddocregion + + // #docregion get_documents_post_1 + await client.index('movies').getDocuments( + params: DocumentsQuery( + filterExpression: Meili.and([ + 'language'.toMeiliAttribute().eq('English'.toMeiliValue()), + Meili.and([ + 'rating'.toMeiliAttribute().gt(3.toMeiliValue()), + Meili.or([ + 'genres'.toMeiliAttribute().eq('Adventure'.toMeiliValue()), + 'genres'.toMeiliAttribute().eq('Fiction'.toMeiliValue()), + ]), + ]), + ]), + fields: ['title', 'genres', 'rating', 'language'], + limit: 3, + ), + ); + // #enddocregion + }, + skip: true, + ); } diff --git a/test/multi_index_search_test.dart b/test/multi_index_search_test.dart index 7aabbc7..09c780b 100644 --- a/test/multi_index_search_test.dart +++ b/test/multi_index_search_test.dart @@ -46,4 +46,14 @@ void main() { expect(result.results.last.hits.length, 2); }); }); + + test('multisearch code samples', () async { + // #docregion multi_search_1 + await client.multiSearch(MultiSearchQuery(queries: [ + IndexSearchQuery(query: 'pooh', indexUid: 'movies', limit: 5), + IndexSearchQuery(query: 'nemo', indexUid: 'movies', limit: 5), + IndexSearchQuery(query: 'us', indexUid: 'movies_ratings'), + ])); + // #enddocregion + }); } diff --git a/test/search_test.dart b/test/search_test.dart index 0fbb14f..45a1768 100644 --- a/test/search_test.dart +++ b/test/search_test.dart @@ -604,4 +604,102 @@ void main() { ); }); }); + + test('search code samples', () async { + // #docregion search_get_1 + await client.index('movies').search('American ninja'); + // #enddocregion + + // #docregion search_parameter_guide_show_ranking_score_1 + await client + .index('movies') + .search('dragon', SearchQuery(showRankingScore: true)); + // #enddocregion + }, skip: true); + + test('facet search code samples', () async { + // #docregion facet_search_1 + await client.index('books').facetSearch( + FacetSearchQuery( + facetQuery: 'fiction', + facetName: 'genres', + filter: 'rating > 3', + ), + ); + // #enddocregion + + // #docregion facet_search_2 + await client.index('books').updateFaceting( + Faceting( + sortFacetValuesBy: { + 'genres': FacetingSortTypes.count, + }, + ), + ); + // #enddocregion + + // #docregion facet_search_3 + await client.index('books').facetSearch( + FacetSearchQuery( + facetQuery: 'c', + facetName: 'genres', + ), + ); + // #enddocregion + + // #docregion search_parameter_guide_attributes_to_search_on_1 + await client.index('books').facetSearch( + FacetSearchQuery( + facetQuery: 'c', + facetName: 'genres', + ), + ); + // #enddocregion + + // #docregion search_parameter_guide_facet_stats_1 + await client + .index('movie_ratings') + .search('Batman', SearchQuery(facets: ['genres', 'rating'])); + // #enddocregion + + // #docregion faceted_search_1 + await client + .index('books') + .search('', SearchQuery(facets: ['genres', 'rating', 'language'])); + // #enddocregion + + // #docregion filtering_guide_nested_1 + await client.index('movie_ratings').search( + 'thriller', + SearchQuery( + filterExpression: Meili.gte( + //or Meili.attr('rating.users') + //or 'rating.users'.toMeiliAttribute() + Meili.attrFromParts(['rating', 'users']), + Meili.value(90), + ), + ), + ); + // #enddocregion + + // #docregion sorting_guide_sort_nested_1 + await client + .index('movie_ratings') + .search('thriller', SearchQuery(sort: ['rating.users:asc'])); + // #enddocregion + + // #docregion search_parameter_guide_page_1 + await client + .index('movies') + .search('', SearchQuery(page: 2)) + .asPaginatedResult(); + // #enddocregion + + // #docregion search_parameter_guide_hitsperpage_1 + await client + .index('movies') + .search('', SearchQuery(hitsPerPage: 15)) + .asPaginatedResult(); + // #enddocregion + }, skip: true); } diff --git a/test/settings_test.dart b/test/settings_test.dart index 0c457e4..c6d9862 100644 --- a/test/settings_test.dart +++ b/test/settings_test.dart @@ -400,4 +400,19 @@ void main() { }); }); }); + + test('code samples', () async { + // #docregion faceted_search_update_settings_1 + await client + .index('movie_ratings') + .updateFilterableAttributes(['genres', 'rating', 'language']); + // #enddocregion + + // #docregion synonyms_guide_1 + await client.index('movies').updateSynonyms({ + 'great': ['fantastic'], + 'fantastic': ['great'], + }); + // #enddocregion + }); } diff --git a/test/swaps_test.dart b/test/swaps_test.dart index f7f4cbd..58ba326 100644 --- a/test/swaps_test.dart +++ b/test/swaps_test.dart @@ -30,4 +30,13 @@ void main() { ]); }); }); + + test('code samples', () async { + // #docregion swap_indexes_1 + await client.swapIndexes([ + SwapIndex(['indexA', 'indexB']), + SwapIndex(['indexX', 'indexY']), + ]); + // #enddocregion + }, skip: true); } diff --git a/test/tasks_test.dart b/test/tasks_test.dart index 7a94e86..f521aeb 100644 --- a/test/tasks_test.dart +++ b/test/tasks_test.dart @@ -66,4 +66,60 @@ void main() { ); }); }); + + test( + 'code samples', + () async { + // #docregion async_guide_filter_by_date_1 + await client.getTasks( + params: TasksQuery( + afterEnqueuedAt: DateTime(2020, 10, 11, 11, 49, 53), + ), + ); + // #enddocregion + // #docregion async_guide_multiple_filters_1 + await client.getTasks( + params: TasksQuery( + indexUids: ['movies'], + types: ['documentAdditionOrUpdate', 'documentDeletion'], + statuses: ['processing'], + ), + ); + // #enddocregion + // #docregion async_guide_filter_by_ids_1 + await client.getTasks( + params: TasksQuery( + uids: [5, 10, 13], + ), + ); + // #enddocregion + // #docregion async_guide_filter_by_statuses_1 + await client.getTasks( + params: TasksQuery( + statuses: ['failed', 'canceled'], + ), + ); + // #enddocregion + // #docregion async_guide_filter_by_types_1 + await client.getTasks( + params: TasksQuery( + types: ['dumpCreation', 'indexSwap'], + ), + ); + // #enddocregion + // #docregion async_guide_filter_by_index_uids_1 + await client.getTasks(params: TasksQuery(indexUids: ['movies'])); + // #enddocregion + // #docregion delete_tasks_1 + await client.deleteTasks(params: DeleteTasksQuery(uids: [1, 2])); + // #enddocregion + // #docregion cancel_tasks_1 + await client.cancelTasks(params: CancelTasksQuery(uids: [1, 2])); + // #enddocregion + // #docregion async_guide_canceled_by_1 + await client.getTasks(params: TasksQuery(canceledBy: [9, 15])); + // #enddocregion + }, + skip: true, + ); } diff --git a/tool/lib/src/update_samples_command.dart b/tool/lib/src/update_samples_command.dart index 86199d3..5462008 100644 --- a/tool/lib/src/update_samples_command.dart +++ b/tool/lib/src/update_samples_command.dart @@ -22,6 +22,8 @@ class _SourceFile { class UpdateSamplesCommand extends MeiliCommandBase { static const String _failOnChangeFlag = 'fail-on-change'; + static const String _checkRemoteRepoFlag = 'check-remote-repository'; + static const String _generateMissingExcerpts = 'generate-missing-excerpts'; UpdateSamplesCommand( super.packageDirectory, { @@ -32,6 +34,17 @@ class UpdateSamplesCommand extends MeiliCommandBase { help: 'Fail if the command does anything. ' '(Used in CI to ensure excerpts are up to date.)', ); + argParser.addFlag( + _checkRemoteRepoFlag, + hide: true, + help: + 'Check the remote code samples to see if there are missing/useless keys', + ); + argParser.addFlag( + _generateMissingExcerpts, + hide: true, + help: 'Generate entries that are found in code samples, but not in code', + ); } @override @@ -49,6 +62,8 @@ class UpdateSamplesCommand extends MeiliCommandBase { Future run() async { try { final failOnChange = getBoolArg(_failOnChangeFlag); + final checkRemoteRepo = getBoolArg(_checkRemoteRepoFlag); + final generateMissingExcerpts = getBoolArg(_generateMissingExcerpts); //read the samples yaml file final changedKeys = {}; final File samplesFile = @@ -59,12 +74,14 @@ class UpdateSamplesCommand extends MeiliCommandBase { print(samplesYaml.runtimeType); return PackageResult.fail(['samples yaml must be an YamlMap']); } - final fullSamplesYaml = await getFullCorrectSamples(); final newSamplesYaml = YamlEditor(samplesContentRaw); + final foundCodeSamples = {}; + final missingSamples = {}; final sourceFiles = await _discoverSourceFiles(); for (var sourceFile in sourceFiles) { final newValues = _runInFile(sourceFile); + foundCodeSamples.addAll(newValues); sourceFile.result = newValues; for (var element in newValues.entries) { final existingValue = samplesYaml[element.key]; @@ -83,6 +100,7 @@ class UpdateSamplesCommand extends MeiliCommandBase { 'found changed keys: ${changedKeys.keys.toList()}', ]); } + if (!failOnChange) { for (var changedEntry in changedKeys.entries) { newSamplesYaml.update([changedEntry.key], changedEntry.value); @@ -90,31 +108,58 @@ class UpdateSamplesCommand extends MeiliCommandBase { } } - final missingEntries = fullSamplesYaml.entries - .where((element) => !samplesYaml.containsKey(element.key)); - final oldEntries = samplesYaml.entries - .where((element) => !fullSamplesYaml.containsKey(element.key)); + for (var entry in samplesYaml.entries) { + if (foundCodeSamples.containsKey(entry.key)) { + continue; + } + missingSamples[entry.key] = entry.value; + } + if (generateMissingExcerpts) { + final targetFile = packageDirectory + .childDirectory('test') + .childFile('missing_samples.dart'); + final sb = StringBuffer(); - if (failOnChange) { - if (missingEntries.isNotEmpty || oldEntries.isNotEmpty) { - return PackageResult.fail([ - if (missingEntries.isNotEmpty) - 'found the following missing entries: ${missingEntries.map((e) => e.key).join('\n')}', - // for now don't delete old entries - // if (oldEntries.isNotEmpty) - // 'found the following useless entries: ${oldEntries.map((e) => e.key).join('\n')}', - ]); + sb.writeln(r"import 'package:meilisearch/meilisearch.dart';"); + sb.writeln('late MeiliSearchClient client;'); + sb.writeln('void main() async {'); + for (var element in missingSamples.entries) { + sb.writeln('// #docregion ${element.key}'); + sb.writeln(element.value); + sb.writeln('// #enddocregion'); + sb.writeln(); } - } else { - for (var element in missingEntries) { - newSamplesYaml.update([element.key], element.value); + sb.writeln('}'); + await targetFile.writeAsString(sb.toString()); + } + + // for now don't check remote repository + if (checkRemoteRepo) { + final fullSamplesYaml = await getFullCorrectSamples(); + final missingEntries = fullSamplesYaml.entries + .where((element) => !samplesYaml.containsKey(element.key)); + final oldEntries = samplesYaml.entries + .where((element) => !fullSamplesYaml.containsKey(element.key)); + if (failOnChange) { + if (missingEntries.isNotEmpty || oldEntries.isNotEmpty) { + return PackageResult.fail([ + if (missingEntries.isNotEmpty) + 'found the following missing entries: ${missingEntries.map((e) => e.key).join('\n')}', + if (oldEntries.isNotEmpty) + 'found the following useless entries: ${oldEntries.map((e) => e.key).join('\n')}', + ]); + } + } else { + for (var element in missingEntries) { + newSamplesYaml.update([element.key], element.value); + } + for (var element in oldEntries) { + newSamplesYaml.remove([element.key]); + } } - // for now don't delete old entries - // for (var element in oldEntries) { - // newSamplesYaml.remove([element.key]); - // } } - if (!failOnChange) { + + if (!failOnChange && !generateMissingExcerpts) { await samplesFile.writeAsString(newSamplesYaml.toString()); } return PackageResult.success(); @@ -156,11 +201,10 @@ class UpdateSamplesCommand extends MeiliCommandBase { } else { if (line.contains(enddocregion)) { final sb = StringBuffer(); - - unindentLines(currentKeyLines.map((e) => e.value).toList()) - .take(currentKeyLines.length - 1) - .forEach(sb.writeln); - sb.write(currentKeyLines.last.value); + final unindentedLines = + unindentLines(currentKeyLines.map((e) => e.value).toList()) + .join('\n'); + sb.write(unindentedLines); //add to results. res[currentKey] = sb.toString(); @@ -184,6 +228,9 @@ class UpdateSamplesCommand extends MeiliCommandBase { final res = []; for (var element in src) { final trimmedLine = element.trimLeft(); + if (trimmedLine.isEmpty) { + continue; + } var indentation = element.length - trimmedLine.length; indentation -= firstIndentation; res.add('${" " * indentation}$trimmedLine'); From 03209bb445dd22774424d108710991d2177817e3 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 19:31:32 +0300 Subject: [PATCH 18/23] fix: MeiliDateTimeValueExpression should calculate seconds since epoch, not millisecondsSinceEpoch --- lib/src/filter_builder/values.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/filter_builder/values.dart b/lib/src/filter_builder/values.dart index c1a8a19..db1f6d4 100644 --- a/lib/src/filter_builder/values.dart +++ b/lib/src/filter_builder/values.dart @@ -29,8 +29,10 @@ class MeiliDateTimeValueExpression extends MeiliValueExpressionBase { "DateTime passed to Meili must be in UTC to avoid inconsistency accross multiple devices", ); + /// Unix epoch time is seconds since epoch @override - String transform() => value.millisecondsSinceEpoch.toString(); + String transform() => + (value.millisecondsSinceEpoch / 1000).floor().toString(); @override bool operator ==(Object other) { From 1ad1bb9cb1696384a14654b6afa66dfdbd7185fa Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Thu, 21 Sep 2023 19:35:07 +0300 Subject: [PATCH 19/23] fix datetime tests --- test/filter_builder_test.dart | 2 +- test/multi_index_search_test.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/filter_builder_test.dart b/test/filter_builder_test.dart index 86431fc..fe962c8 100644 --- a/test/filter_builder_test.dart +++ b/test/filter_builder_test.dart @@ -75,7 +75,7 @@ void main() { }); test("Dates", () { final testData = [ - [DateTime.utc(1999, 12, 14, 18, 53, 56), '945197636000'], + [DateTime.utc(1999, 12, 14, 18, 53, 56), '945197636'], ]; for (var element in testData) { diff --git a/test/multi_index_search_test.dart b/test/multi_index_search_test.dart index 09c780b..64abd3f 100644 --- a/test/multi_index_search_test.dart +++ b/test/multi_index_search_test.dart @@ -47,7 +47,7 @@ void main() { }); }); - test('multisearch code samples', () async { + test('code samples', () async { // #docregion multi_search_1 await client.multiSearch(MultiSearchQuery(queries: [ IndexSearchQuery(query: 'pooh', indexUid: 'movies', limit: 5), @@ -55,5 +55,5 @@ void main() { IndexSearchQuery(query: 'us', indexUid: 'movies_ratings'), ])); // #enddocregion - }); + }, skip: true); } From e617174d25d65c736c85a782076576a75f8c4b0f Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Fri, 22 Sep 2023 04:38:57 +0300 Subject: [PATCH 20/23] update mismatched keys --- .code-samples.meilisearch.yaml | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 2dc1af4..db106fe 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -207,27 +207,13 @@ typo_tolerance_guide_4: |- await client.index('movies').updateTypoTolerance(toUpdate); getting_started_add_documents_md: "```bash\ndart pub add meilisearch\n```\n```dart\nimport 'package:meilisearch\/meilisearch.dart';\nimport 'dart:io';\nimport 'dart:convert';\nvar client = MeiliSearchClient('http:\/\/localhost:7700', 'aSampleMasterKey');\nfinal json = await File('movies.json').readAsString();\nawait client.index('movies').addDocumentsJson(json);\n```\n[About this SDK](https:\/\/github.com\/meilisearch\/meilisearch-dart\/)" getting_started_search_md: "```dart\nawait client.index('movies').search('botman');\n```\n[About this SDK](https:\/\/github.com\/meilisearch\/meilisearch-dart\/)" -getting_started_update_ranking_rules: |- - await client.index('movies').updateRankingRules([ - 'exactness', - 'words', - 'typo', - 'proximity', - 'attribute', - 'sort', - 'release_date:asc', - 'rank:desc' - ]); +getting_started_update_ranking_rules: "await client.index('movies').updateRankingRules([\n 'exactness',\n 'words',\n 'typo',\n 'proximity',\n 'attribute',\n 'sort',\n 'release_date:asc',\n 'rank:desc',\n]);" getting_started_update_searchable_attributes: "await client.index('movies').updateSearchableAttributes(['title']);" getting_started_update_stop_words: |- await client.index('movies').updateStopWords(['the']); getting_started_check_task_status: |- await client.getTask(0); -getting_started_synonyms: |- - await client.index('movies').updateSynonyms({ - 'winnie': ['piglet'], - 'piglet': ['winnie'] - }); +getting_started_synonyms: "await client.index('movies').updateSynonyms({\n 'winnie': ['piglet'],\n 'piglet': ['winnie'],\n});" getting_started_update_displayed_attributes: "await client\n .index('movies')\n .updateDisplayedAttributes(['title', 'overview', 'poster']);" getting_started_add_meteorites: "final json = await File('meteorites.json').readAsString();\nawait client.index('meteorites').addDocumentsJson(json);" getting_started_configure_settings: "await client.index('meteorites').updateSettings(IndexSettings(\n filterableAttributes: ['mass', '_geo'],\n sortableAttributes: ['mass', '_geo']));" From bf1f5d47130c6edddb598edc9bbb95dfa7cce703 Mon Sep 17 00:00:00 2001 From: meili-bot <74670311+meili-bot@users.noreply.github.com> Date: Sat, 18 Nov 2023 21:03:05 +0100 Subject: [PATCH 21/23] Update README.md (#365) --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index c3a7afa..85e4a39 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,7 @@ **Meilisearch** is an open-source search engine. [Learn more about Meilisearch.](https://github.com/meilisearch/meilisearch) ---- - -### 🔥 On November 2nd, we are hosting our first-ever live demo and product updates for [Meilisearch Cloud](https://www.meilisearch.com/cloud?utm_campaign=oss&utm_source=github&utm_medium=meilisearch). Make sure to [register here](https://us06web.zoom.us/meeting/register/tZMlc-mqrjIsH912-HTRe-AaT-pp41bDe81a#/registration) and bring your questions for live Q&A! - ---- - -## Table of Contents +## Table of Contents - [📖 Documentation](#-documentation) - [⚡ Supercharge your Meilisearch experience](#-supercharge-your-meilisearch-experience) From 92ed56c3266cd42561c9bbb47499ff5cb68f1d8d Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Wed, 22 Nov 2023 21:46:47 +0200 Subject: [PATCH 22/23] Update contributing guide --- CONTRIBUTING.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c790302..eecd17b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,7 @@ First of all, thank you for contributing to Meilisearch! The goal of this docume - [Requirements ](#requirements-) - [Setup ](#setup-) - [Tests and Linter ](#tests-and-linter-) + - [Updating code samples](#updating-code-samples) - [Git Guidelines](#git-guidelines) - [Git Branches ](#git-branches-) - [Git Commits ](#git-commits-) @@ -76,6 +77,31 @@ dart test dart analyze ``` +### Updating code samples + +Some PRs require updating the code samples (found in `.code-samples.meilisearch.yaml`), this is done automatically using code excerpts, which are actual pieces of code subject to testing and linting. + +A lot of them are placed in `test/code_samples.dart`. + +Also most of the tests in that file are skipped, since they are mostly duplicated in other test files. + +The process to define a new code sample is as follows: +1. Add the piece of code in `test/code_samples.dart` +2. surround it with `#docregion key` and `#enddocregion`, e.g. + ``` + // #docregion meilisearch_contributing_1 + final client = MeilisearchClient(); + anything(); + // #enddocregion + ``` +3. run this command to update the code samples + ```bash + dart run ./tool/bin/meili.dart update-samples + ``` +4. to test if the code samples are updated correctly, run: + ```bash + dart run ./tool/bin/meili.dart update-samples --fail-on-change + ``` ## Git Guidelines ### Git Branches From 228a95b9a8689c5b83472cb3a4946d51d9cbbe2a Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Wed, 22 Nov 2023 21:51:51 +0200 Subject: [PATCH 23/23] fix test message changing --- test/exceptions_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/exceptions_test.dart b/test/exceptions_test.dart index 3981a7d..6975cbf 100644 --- a/test/exceptions_test.dart +++ b/test/exceptions_test.dart @@ -25,7 +25,7 @@ void main() { throwsA(isA().having( (error) => error.toString(), // Actual 'toString() method', // Description of the check - 'MeiliSearchApiError - message: The request returned an invalid status code of 404.', // Expected + contains('404'), // Expected )), ); });