-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8568bdf
commit 66b49b7
Showing
10 changed files
with
256 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:async/async.dart'; | ||
|
||
abstract class FileDescriptorInterface { | ||
Stream<List<int>> open(String eTag); | ||
String get etag; | ||
dynamic decode(String line); | ||
dynamic decodeKey(String line); | ||
} | ||
|
||
class FileDescriptor implements FileDescriptorInterface { | ||
final File _file; | ||
final Uri _uri; | ||
final Decode _decode; | ||
final DecodeKey _decodeKey; | ||
String _eTag; | ||
|
||
FileDescriptor.file(File file, {Decode decode, DecodeKey decodeKey}) | ||
: _file = file, | ||
_uri = null, | ||
_decode = decode, | ||
_decodeKey = decodeKey; | ||
|
||
FileDescriptor.uri(Uri uri, {Decode decode, DecodeKey decodeKey}) | ||
: _file = null, | ||
_uri = uri, | ||
_decode = decode, | ||
_decodeKey = decodeKey; | ||
|
||
Stream<List<int>> open(String eTag) { | ||
if (_file != null) { | ||
return LazyStream(() async { | ||
_eTag = (await _file.lastModified()).millisecondsSinceEpoch.toString(); | ||
return _file.openRead(); | ||
}); | ||
} | ||
|
||
if (_uri != null) { | ||
return LazyStream(() async { | ||
final request = await HttpClient().getUrl(_uri); | ||
request.headers.set(HttpHeaders.ifNoneMatchHeader, eTag); | ||
|
||
final response = await request.close(); | ||
_eTag = response.headers.value(HttpHeaders.etagHeader); | ||
return response; | ||
}); | ||
} | ||
|
||
throw StateError('Invalid FileDescriptor'); | ||
} | ||
|
||
String get etag { | ||
if (_eTag != null) return _eTag; | ||
throw StateError('etag must be called after open()'); | ||
} | ||
|
||
dynamic decode(String line) => _decode(line); | ||
dynamic decodeKey(String line) => _decodeKey(line); | ||
} | ||
|
||
typedef dynamic Decode(String line); | ||
typedef dynamic DecodeKey(String line); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import 'dart:convert'; | ||
|
||
import '../../handlers/dynamic_mirror_handler.dart'; | ||
import '../../handlers/handler_holder.dart'; | ||
import '../../hive_mirror.dart'; | ||
import '../../metadata.dart'; | ||
import '../mirror_manager.dart'; | ||
import 'file_descriptor.dart'; | ||
|
||
class FileMirrorManager implements MirrorManager { | ||
final MirrorHandlerHolder _handler; | ||
final Metadata _metadata; | ||
|
||
FileMirrorManager(MirrorHandler handler, Metadata metadata) | ||
: _handler = MirrorHandlerHolder(handler), | ||
_metadata = metadata; | ||
|
||
static FileMirrorManager withHandler<T>( | ||
MirrorHandler<T> handler, Metadata metadata) { | ||
return FileMirrorManager(DynamicMirrorHandler<T>(handler), metadata); | ||
} | ||
|
||
Future<void> mirror(dynamic fileDescriptor) => loadFile(fileDescriptor); | ||
|
||
Future<void> loadFile(FileDescriptorInterface fileDescriptor) async { | ||
final etag = _metadata.get(metaEtag); | ||
final fileData = fileDescriptor.open(etag); | ||
|
||
if (fileDescriptor.etag != etag) { | ||
final lines = fileData.transform(Utf8Decoder()).transform(LineSplitter()); | ||
|
||
try { | ||
await _applyLines(fileDescriptor.etag, await lines.toList(), | ||
fileDescriptor.decode, fileDescriptor.decodeKey); | ||
} finally { | ||
await _handler.dispose(); | ||
} | ||
} | ||
} | ||
|
||
Future<void> _applyLines(String etag, Iterable<String> lines, Decode decode, | ||
DecodeKey decodeKey) async { | ||
MapEntry decodeLine(String line) { | ||
final key = decodeKey(line); | ||
if (key != null) { | ||
final object = decode(line); | ||
return MapEntry(key, object); | ||
} | ||
return null; | ||
} | ||
|
||
final putEntries = | ||
Map.fromEntries(lines.map(decodeLine).where((e) => e != null)); | ||
|
||
await (await _handler.use()).clear(); | ||
|
||
if (putEntries.isNotEmpty) { | ||
await (await _handler.use()).putAll(putEntries); | ||
} | ||
|
||
await _metadata.put(metaEtag, etag); | ||
} | ||
|
||
static const metaEtag = 'etag'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,26 @@ | ||
import 'package:hive_mirror/src/handlers/handler_holder.dart'; | ||
|
||
import '../hive_mirror.dart'; | ||
import '../metadata.dart'; | ||
import 'file/file_descriptor.dart'; | ||
import 'file/file_mirror_manager.dart'; | ||
import 'git/git_mirror_manager.dart'; | ||
import 'git/git_patch.dart'; | ||
|
||
abstract class MirrorManager { | ||
Future<void> mirror(dynamic source); | ||
|
||
factory MirrorManager.fromSource(dynamic source, | ||
{MirrorHandler handler, Metadata metadata}) { | ||
factory MirrorManager.fromSource( | ||
dynamic source, { | ||
MirrorHandler handler, | ||
Metadata metadata, | ||
}) { | ||
if (source is GitPatchInterface) { | ||
return GitMirrorManager(handler, metadata); | ||
} | ||
throw UnsupportedError('''source $source is not supported. | ||
Use one of the supported sources [GitPatch]'''); | ||
if (source is FileDescriptorInterface) { | ||
return FileMirrorManager(handler, metadata); | ||
} | ||
throw ArgumentError('''source $source is not supported. | ||
Use one of the supported sources | ||
[GitPatchInterface, FileDescriptorInterface]'''); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
key1: value1 | ||
key2: value2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:hive_mirror/src/backend/file/file_descriptor.dart'; | ||
|
||
import 'type.dart'; | ||
|
||
abstract class TestFileDescriptorBase implements FileDescriptorInterface { | ||
@override | ||
final etag; | ||
|
||
final String _filePath; | ||
final Decode _decode; | ||
final DecodeKey _decodeKey; | ||
|
||
TestFileDescriptorBase._( | ||
this.etag, this._filePath, this._decode, this._decodeKey); | ||
|
||
@override | ||
Stream<List<int>> open(String _) => File(_filePath).openRead(); | ||
|
||
@override | ||
dynamic decode(String line) => _decode(line); | ||
|
||
@override | ||
dynamic decodeKey(String line) => _decodeKey(line); | ||
} | ||
|
||
class Load2FileDescriptor extends TestFileDescriptorBase { | ||
Load2FileDescriptor.primitive() | ||
: super._(etagValue, filePath, _decodePrimitive, _decodeKey); | ||
Load2FileDescriptor.testType() | ||
: super._(etagValue, filePath, _decodeTestType, _decodeKey); | ||
|
||
static const filePath = 'test/assets/load2.yaml'; | ||
static const etagValue = 'etag_value'; | ||
static const loadMap = {'key1': 'value1', 'key2': 'value2'}; | ||
} | ||
|
||
String _decodePrimitive(String line) { | ||
final tupple = line.split(':'); | ||
return tupple[1].trim(); | ||
} | ||
|
||
TestType _decodeTestType(String line) { | ||
final tupple = line.split(':'); | ||
return TestType(tupple[0].trim(), tupple[1].trim()); | ||
} | ||
|
||
String _decodeKey(String line) { | ||
final tupple = line.split(':'); | ||
return tupple[0].trim(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import 'package:hive_mirror/src/backend/file/file_mirror_manager.dart'; | ||
import 'package:mockito/mockito.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
import '../../../file_descriptors.dart'; | ||
import '../../mocks.dart'; | ||
|
||
const metaETag = FileMirrorManager.metaEtag; | ||
|
||
void main() { | ||
group('FileMirrorManager load()', () { | ||
test('file with new etag', () async { | ||
final handler = MirrorHandlerMock(); | ||
final metadata = MetadataMock(); | ||
final manager = FileMirrorManager.withHandler(handler, metadata); | ||
|
||
await manager.loadFile(Load2FileDescriptor.primitive()); | ||
|
||
verify(metadata.get(metaETag)); | ||
verify(handler.clear()); | ||
verify(handler.putAll(argThat(equals(Load2FileDescriptor.loadMap)))); | ||
verify(metadata.put(metaETag, Load2FileDescriptor.etagValue)); | ||
}); | ||
|
||
test('file with previous etag', () async { | ||
final handler = MirrorHandlerMock(); | ||
final metadata = MetadataMock(); | ||
final manager = FileMirrorManager.withHandler(handler, metadata); | ||
|
||
when(metadata.get(metaETag)).thenReturn(Load2FileDescriptor.etagValue); | ||
|
||
await manager.loadFile(Load2FileDescriptor.primitive()); | ||
|
||
verify(metadata.get(metaETag)); | ||
verifyNever(handler.clear()); | ||
verifyNever(handler.putAll(any)); | ||
verifyNever(metadata.put(any, any)); | ||
}); | ||
}); | ||
} |