http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_message_reader.dart ---------------------------------------------------------------------- diff --git a/lib/dart/lib/src/transport/t_message_reader.dart b/lib/dart/lib/src/transport/t_message_reader.dart new file mode 100644 index 0000000..8ca0708 --- /dev/null +++ b/lib/dart/lib/src/transport/t_message_reader.dart @@ -0,0 +1,99 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +part of thrift; + +/// [TMessageReader] extracts a [TMessage] from bytes. This is used to allow a +/// transport to inspect the message seqid and map responses to requests. +class TMessageReader { + final TProtocolFactory protocolFactory; + + final int byteOffset; + final _TMessageReaderTransport _transport; + + /// Construct a [MessageReader]. The optional [byteOffset] specifies the + /// number of bytes to skip before reading the [TMessage]. + TMessageReader(this.protocolFactory, {int byteOffset: 0}) + : _transport = new _TMessageReaderTransport(), + this.byteOffset = byteOffset; + + TMessage readMessage(Uint8List bytes) { + _transport.reset(bytes, byteOffset); + TProtocol protocol = protocolFactory.getProtocol(_transport); + TMessage message = protocol.readMessageBegin(); + _transport.reset(null); + + return message; + } +} + +/// An internal class used to support [TMessageReader]. +class _TMessageReaderTransport extends TTransport { + _TMessageReaderTransport(); + + Iterator<int> _readIterator; + + void reset(Uint8List bytes, [int offset = 0]) { + if (bytes == null) { + _readIterator = null; + return; + } + + if (offset > bytes.length) { + throw new ArgumentError("The offset exceeds the bytes length"); + } + + _readIterator = bytes.iterator; + + for (var i = 0; i < offset; i++) { + _readIterator.moveNext(); + } + } + + get isOpen => true; + + Future open() => throw new UnsupportedError("Unsupported in MessageReader"); + + Future close() => throw new UnsupportedError("Unsupported in MessageReader"); + + int read(Uint8List buffer, int offset, int length) { + if (buffer == null) { + throw new ArgumentError.notNull("buffer"); + } + + if (offset + length > buffer.length) { + throw new ArgumentError("The range exceeds the buffer length"); + } + + if (_readIterator == null || length <= 0) { + return 0; + } + + int i = 0; + while (i < length && _readIterator.moveNext()) { + buffer[offset + i] = _readIterator.current; + i++; + } + + return i; + } + + void write(Uint8List buffer, int offset, int length) => + throw new UnsupportedError("Unsupported in MessageReader"); + + Future flush() => throw new UnsupportedError("Unsupported in MessageReader"); +}
http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_socket.dart ---------------------------------------------------------------------- diff --git a/lib/dart/lib/src/transport/t_socket.dart b/lib/dart/lib/src/transport/t_socket.dart new file mode 100644 index 0000000..74618b6 --- /dev/null +++ b/lib/dart/lib/src/transport/t_socket.dart @@ -0,0 +1,38 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +part of thrift; + +enum TSocketState { CLOSED, OPEN } + +abstract class TSocket { + Stream<TSocketState> get onState; + + Stream<String> get onError; + + Stream<Uint8List> get onMessage; + + bool get isOpen; + + bool get isClosed; + + Future open(); + + Future close(); + + void send(Uint8List data); +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_socket_transport.dart ---------------------------------------------------------------------- diff --git a/lib/dart/lib/src/transport/t_socket_transport.dart b/lib/dart/lib/src/transport/t_socket_transport.dart new file mode 100644 index 0000000..ad7e48e --- /dev/null +++ b/lib/dart/lib/src/transport/t_socket_transport.dart @@ -0,0 +1,175 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +part of thrift; + +/// Socket implementation of [TTransport]. +/// +/// For example: +/// +/// var transport = new TClientSocketTransport(new TWebSocket(url)); +/// var protocol = new TBinaryProtocol(transport); +/// var client = new MyThriftServiceClient(protocol); +/// var result = client.myMethod(); +/// +/// Adapted from the JS WebSocket transport. +abstract class TSocketTransport extends TBufferedTransport { + final Logger log = new Logger('thrift.TSocketTransport'); + + final TSocket socket; + + /// A transport using the provided [socket]. + TSocketTransport(this.socket) { + if (socket == null) { + throw new ArgumentError.notNull('socket'); + } + + socket.onError.listen((String e) => log.warning(e)); + socket.onMessage.listen(handleIncomingMessage); + } + + bool get isOpen => socket.isOpen; + + Future open() { + _reset(isOpen: true); + return socket.open(); + } + + Future close() { + _reset(isOpen: false); + return socket.close(); + } + + /// Make an incoming message available to read from the transport. + void handleIncomingMessage(Uint8List messageBytes) { + _setReadBuffer(messageBytes); + } +} + +/// [TClientSocketTransport] is a basic client socket transport. It sends +/// outgoing messages and expects a response. +/// +/// NOTE: This transport expects a single threaded server, as it will process +/// responses in FIFO order. +class TClientSocketTransport extends TSocketTransport { + final List<Completer<Uint8List>> _completers = []; + + TClientSocketTransport(TSocket socket) : super(socket); + + Future flush() { + Uint8List bytes = _consumeWriteBuffer(); + + // Use a sync completer to ensure that the buffer can be read immediately + // after the read buffer is set, and avoid a race condition where another + // response could overwrite the read buffer. + Completer completer = new Completer.sync(); + _completers.add(completer); + + socket.send(bytes); + + return completer.future; + } + + void handleIncomingMessage(Uint8List messageBytes) { + super.handleIncomingMessage(messageBytes); + + if (_completers.isNotEmpty) { + var completer = _completers.removeAt(0); + completer.complete(); + } + } +} + +/// [TAsyncClientSocketTransport] sends outgoing messages and expects an +/// asynchronous response. +/// +/// NOTE: This transport uses a [MessageReader] to read a [TMessage] when an +/// incoming message arrives to correlate a response to a request, using the +/// seqid. +class TAsyncClientSocketTransport extends TSocketTransport { + static const defaultTimeout = const Duration(seconds: 30); + + final Map<int, Completer<Uint8List>> _completers = {}; + + final TMessageReader messageReader; + + final Duration responseTimeout; + + TAsyncClientSocketTransport(TSocket socket, TMessageReader messageReader, + {Duration responseTimeout: defaultTimeout}) + : this.messageReader = messageReader, + this.responseTimeout = responseTimeout, + super(socket); + + Future flush() { + Uint8List bytes = _consumeWriteBuffer(); + TMessage message = messageReader.readMessage(bytes); + int seqid = message.seqid; + + // Use a sync completer to ensure that the buffer can be read immediately + // after the read buffer is set, and avoid a race condition where another + // response could overwrite the read buffer. + Completer completer = new Completer.sync(); + _completers[seqid] = completer; + + if (responseTimeout != null) { + new Future.delayed(responseTimeout, () { + var completer = _completers.remove(seqid); + if (completer != null) { + completer.completeError( + new TimeoutException("Response timed out.", responseTimeout)); + } + }); + } + + socket.send(bytes); + + return completer.future; + } + + void handleIncomingMessage(Uint8List messageBytes) { + super.handleIncomingMessage(messageBytes); + + TMessage message = messageReader.readMessage(messageBytes); + var completer = _completers.remove(message.seqid); + if (completer != null) { + completer.complete(); + } + } +} + +/// [TServerSocketTransport] listens for incoming messages. When it sends a +/// response, it does not expect an acknowledgement. +class TServerSocketTransport extends TSocketTransport { + final StreamController _onIncomingMessageController; + Stream get onIncomingMessage => _onIncomingMessageController.stream; + + TServerSocketTransport(TSocket socket) + : _onIncomingMessageController = new StreamController.broadcast(), + super(socket); + + Future flush() async { + Uint8List message = _consumeWriteBuffer(); + socket.send(message); + } + + void handleIncomingMessage(Uint8List messageBytes) { + super.handleIncomingMessage(messageBytes); + + _onIncomingMessageController.add(null); + } +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_transport.dart ---------------------------------------------------------------------- diff --git a/lib/dart/lib/src/transport/t_transport.dart b/lib/dart/lib/src/transport/t_transport.dart new file mode 100644 index 0000000..563d5eb --- /dev/null +++ b/lib/dart/lib/src/transport/t_transport.dart @@ -0,0 +1,70 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +part of thrift; + +abstract class TTransport { + /// Queries whether the transport is open. + /// Returns [true] if the transport is open. + bool get isOpen; + + /// Opens the transport for reading/writing. + /// Throws [TTransportError] if the transport could not be opened. + Future open(); + + /// Closes the transport. + Future close(); + + /// Reads up to [length] bytes into [buffer], starting at [offset]. + /// Returns the number of bytes actually read. + /// Throws [TTransportError] if there was an error reading data + int read(Uint8List buffer, int offset, int length); + + /// Guarantees that all of [length] bytes are actually read off the transport. + /// Returns the number of bytes actually read, which must be equal to + /// [length]. + /// Throws [TTransportError] if there was an error reading data + int readAll(Uint8List buffer, int offset, int length) { + int got = 0; + int ret = 0; + while (got < length) { + ret = read(buffer, offset + got, length - got); + if (ret <= 0) { + throw new TTransportError( + TTransportErrorType.UNKNOWN, + "Cannot read. Remote side has closed. Tried to read $length " + "bytes, but only got $got bytes."); + } + got += ret; + } + return got; + } + + /// Writes up to [len] bytes from the buffer. + /// Throws [TTransportError] if there was an error writing data + void write(Uint8List buffer, int offset, int length); + + /// Writes the [bytes] to the output. + /// Throws [TTransportError] if there was an error writing data + void writeAll(Uint8List buffer) { + write(buffer, 0, buffer.length); + } + + /// Flush any pending data out of a transport buffer. + /// Throws [TTransportError] if there was an error writing out data. + Future flush(); +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_transport_error.dart ---------------------------------------------------------------------- diff --git a/lib/dart/lib/src/transport/t_transport_error.dart b/lib/dart/lib/src/transport/t_transport_error.dart new file mode 100644 index 0000000..d3508e0 --- /dev/null +++ b/lib/dart/lib/src/transport/t_transport_error.dart @@ -0,0 +1,31 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +part of thrift; + +class TTransportErrorType { + static const int UNKNOWN = 0; + static const int NOT_OPEN = 1; + static const int ALREADY_OPEN = 2; + static const int TIMED_OUT = 3; + static const int END_OF_FILE = 4; +} + +class TTransportError extends TError { + TTransportError([int type = TTransportErrorType.UNKNOWN, String message = ""]) + : super(type, message); +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/src/transport/t_transport_factory.dart ---------------------------------------------------------------------- diff --git a/lib/dart/lib/src/transport/t_transport_factory.dart b/lib/dart/lib/src/transport/t_transport_factory.dart new file mode 100644 index 0000000..7a10461 --- /dev/null +++ b/lib/dart/lib/src/transport/t_transport_factory.dart @@ -0,0 +1,27 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +part of thrift; + +/// Factory class used to create wrapped instance of a [TTransport]. This is +/// used primarily in servers. +/// +/// Adapted from the Java version. +class TTransportFactory { + Future<TTransport> getTransport(TTransport transport) => + new Future.value(transport); +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/thrift.dart ---------------------------------------------------------------------- diff --git a/lib/dart/lib/thrift.dart b/lib/dart/lib/thrift.dart new file mode 100644 index 0000000..2483726 --- /dev/null +++ b/lib/dart/lib/thrift.dart @@ -0,0 +1,58 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +library thrift; + +import 'dart:async'; +import 'dart:convert' show Utf8Codec; +import 'dart:typed_data' show ByteData; +import 'dart:typed_data' show Uint8List; + +import 'package:crypto/crypto.dart' show CryptoUtils; +import 'package:http/http.dart' show Client; +import 'package:logging/logging.dart'; + +part 'src/t_application_error.dart'; +part 'src/t_base.dart'; +part 'src/t_error.dart'; +part 'src/t_processor.dart'; + +part 'src/protocol/t_binary_protocol.dart'; +part 'src/protocol/t_field.dart'; +part 'src/protocol/t_json_protocol.dart'; +part 'src/protocol/t_list.dart'; +part 'src/protocol/t_map.dart'; +part 'src/protocol/t_message.dart'; +part 'src/protocol/t_multiplexed_protocol.dart'; +part 'src/protocol/t_protocol.dart'; +part 'src/protocol/t_protocol_decorator.dart'; +part 'src/protocol/t_protocol_error.dart'; +part 'src/protocol/t_protocol_factory.dart'; +part 'src/protocol/t_protocol_util.dart'; +part 'src/protocol/t_set.dart'; +part 'src/protocol/t_struct.dart'; +part 'src/protocol/t_type.dart'; + +part 'src/transport/t_buffered_transport.dart'; +part 'src/transport/t_framed_transport.dart'; +part 'src/transport/t_http_transport.dart'; +part 'src/transport/t_message_reader.dart'; +part 'src/transport/t_socket.dart'; +part 'src/transport/t_transport.dart'; +part 'src/transport/t_transport_error.dart'; +part 'src/transport/t_transport_factory.dart'; +part 'src/transport/t_socket_transport.dart'; http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/thrift_browser.dart ---------------------------------------------------------------------- diff --git a/lib/dart/lib/thrift_browser.dart b/lib/dart/lib/thrift_browser.dart new file mode 100644 index 0000000..2ebc257 --- /dev/null +++ b/lib/dart/lib/thrift_browser.dart @@ -0,0 +1,22 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +library thrift_browser; + +/// Classes that are only supported in browser applications go here + +export 'src/browser/t_web_socket.dart' show TWebSocket; http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/lib/thrift_console.dart ---------------------------------------------------------------------- diff --git a/lib/dart/lib/thrift_console.dart b/lib/dart/lib/thrift_console.dart new file mode 100644 index 0000000..48a83d1 --- /dev/null +++ b/lib/dart/lib/thrift_console.dart @@ -0,0 +1,23 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +library thrift_console; + +/// Classes that are only supported in console applications go here + +export 'src/console/t_tcp_socket.dart' show TTcpSocket; +export 'src/console/t_web_socket.dart' show TWebSocket; http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/pubspec.yaml ---------------------------------------------------------------------- diff --git a/lib/dart/pubspec.yaml b/lib/dart/pubspec.yaml new file mode 100644 index 0000000..f64d980 --- /dev/null +++ b/lib/dart/pubspec.yaml @@ -0,0 +1,36 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# 'License'); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: thrift +version: 1.0.0-dev +description: > + A Dart library for Apache Thrift +author: Mark Erickson <[email protected]> +homepage: https://github.com/apache/thrift +documentation: https://github.com/apache/thrift +environment: + sdk: ">=1.12.0 <2.0.0" +dependencies: + crypto: "^0.9.0" + http: "^0.11.3" + logging: "^0.11.0" +dev_dependencies: + coverage: "^0.7.2" + dart_dev: "^1.0.1" + dart_style: "^0.2.0" + mockito: "^0.11.0" + test: "^0.12.0" http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/test/protocol/t_protocol_test.dart ---------------------------------------------------------------------- diff --git a/lib/dart/test/protocol/t_protocol_test.dart b/lib/dart/test/protocol/t_protocol_test.dart new file mode 100644 index 0000000..88ddd4f --- /dev/null +++ b/lib/dart/test/protocol/t_protocol_test.dart @@ -0,0 +1,374 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +library thrift.test.transport.t_json_protocol_test; + +import 'dart:async'; +import 'dart:typed_data' show Uint8List; + +import 'package:test/test.dart'; +import 'package:thrift/thrift.dart'; + +void main() { + final message = new TMessage('my message', TMessageType.ONEWAY, 123); + + TProtocol protocol; + + Primitive getPrimitive(int tType) { + switch (tType) { + case TType.BOOL: + return new Primitive(protocol.readBool, protocol.writeBool, false); + + case TType.BYTE: + return new Primitive(protocol.readByte, protocol.writeByte, 0); + + case TType.I16: + return new Primitive(protocol.readI16, protocol.writeI16, 0); + + case TType.I32: + return new Primitive(protocol.readI32, protocol.writeI32, 0); + + case TType.I64: + return new Primitive(protocol.readI64, protocol.writeI64, 0); + + case TType.DOUBLE: + return new Primitive(protocol.readDouble, protocol.writeDouble, 0); + + case TType.STRING: + return new Primitive(protocol.readString, protocol.writeString, ''); + + default: + throw new UnsupportedError("Unsupported TType $tType"); + } + } + + Future primitiveTest(Primitive primitive, input) async { + primitive.write(input); + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + protocol.readMessageBegin(); + var output = primitive.read(); + + expect(output, input); + } + + Future primitiveNullTest(Primitive primitive) async { + primitive.write(null); + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + protocol.readMessageBegin(); + var output = primitive.read(); + + expect(output, primitive.defaultValue); + } + + var sharedTests = () { + test('Test message', () async { + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + var subject = protocol.readMessageBegin(); + + expect(subject.name, message.name); + expect(subject.type, message.type); + expect(subject.seqid, message.seqid); + }); + + test('Test struct', () async { + var input = new TStruct(); + + protocol.writeStructBegin(input); + protocol.writeStructEnd(); + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + protocol.readMessageBegin(); + var output = protocol.readStructBegin(); + + // name is not serialized, see C# version for reference + expect(output, isNotNull); + }); + + test('Test field', () async { + var input = new TField('my field', TType.MAP, 123); + + protocol.writeFieldBegin(input); + protocol.writeFieldEnd(); + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + protocol.readMessageBegin(); + var output = protocol.readFieldBegin(); + + // name is not serialized, see C# version for reference + expect(output.type, input.type); + expect(output.id, input.id); + }); + + test('Test map', () async { + var input = new TMap(TType.STRING, TType.STRUCT, 123); + + protocol.writeMapBegin(input); + protocol.writeMapEnd(); + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + protocol.readMessageBegin(); + var output = protocol.readMapBegin(); + + expect(output.keyType, input.keyType); + expect(output.valueType, input.valueType); + expect(output.length, input.length); + }); + + test('Test list', () async { + var input = new TList(TType.STRING, 123); + + protocol.writeListBegin(input); + protocol.writeListEnd(); + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + protocol.readMessageBegin(); + var output = protocol.readListBegin(); + + expect(output.elementType, input.elementType); + expect(output.length, input.length); + }); + + test('Test set', () async { + var input = new TSet(TType.STRING, 123); + + protocol.writeSetBegin(input); + protocol.writeSetEnd(); + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + protocol.readMessageBegin(); + var output = protocol.readListBegin(); + + expect(output.elementType, input.elementType); + expect(output.length, input.length); + }); + + test('Test bool', () async { + await primitiveTest(getPrimitive(TType.BOOL), true); + }); + + test('Test bool null', () async { + await primitiveNullTest(getPrimitive(TType.BOOL)); + }); + + test('Test byte', () async { + await primitiveTest(getPrimitive(TType.BYTE), 64); + }); + + test('Test byte null', () async { + await primitiveNullTest(getPrimitive(TType.BYTE)); + }); + + test('Test I16', () async { + await primitiveTest(getPrimitive(TType.I16), 32767); + }); + + test('Test I16 null', () async { + await primitiveNullTest(getPrimitive(TType.I16)); + }); + + test('Test I32', () async { + await primitiveTest(getPrimitive(TType.I32), 2147483647); + }); + + test('Test I32 null', () async { + await primitiveNullTest(getPrimitive(TType.I32)); + }); + + test('Test I64', () async { + await primitiveTest(getPrimitive(TType.I64), 9223372036854775807); + }); + + test('Test I64 null', () async { + await primitiveNullTest(getPrimitive(TType.I64)); + }); + + test('Test double', () async { + await primitiveTest(getPrimitive(TType.DOUBLE), 3.1415926); + }); + + test('Test double null', () async { + await primitiveNullTest(getPrimitive(TType.DOUBLE)); + }); + + test('Test string', () async { + var input = 'There are only two hard things in computer science: ' + 'cache invalidation, naming things, and off-by-one errors.'; + await primitiveTest(getPrimitive(TType.STRING), input); + }); + + test('Test string null', () async { + await primitiveNullTest(getPrimitive(TType.STRING)); + }); + + test('Test binary', () async { + var input = new Uint8List.fromList(new List.filled(100, 123)); + + protocol.writeBinary(input); + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + protocol.readMessageBegin(); + var output = protocol.readBinary(); + + expect(output.length, input.length); + expect(output.every((i) => i == 123), isTrue); + }); + + test('Test complex struct', () async { + // {1: {10: 20}, 2: {30: 40}} + protocol.writeStructBegin(new TStruct()); + protocol.writeFieldBegin(new TField('success', TType.MAP, 0)); + protocol.writeMapBegin(new TMap(TType.I32, TType.MAP, 2)); + + protocol.writeI32(1); // key + protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1)); + protocol.writeI32(10); // key + protocol.writeI32(20); // value + protocol.writeMapEnd(); + + protocol.writeI32(2); // key + protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1)); + protocol.writeI32(30); // key + protocol.writeI32(40); // value + protocol.writeMapEnd(); + + protocol.writeMapEnd(); + protocol.writeFieldEnd(); + protocol.writeFieldStop(); + protocol.writeStructEnd(); + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + protocol.readMessageBegin(); + protocol.readStructBegin(); + expect(protocol.readFieldBegin().type, TType.MAP); + expect(protocol.readMapBegin().length, 2); + + expect(protocol.readI32(), 1); // key + expect(protocol.readMapBegin().length, 1); + expect(protocol.readI32(), 10); // key + expect(protocol.readI32(), 20); // value + protocol.readMapEnd(); + + expect(protocol.readI32(), 2); // key + expect(protocol.readMapBegin().length, 1); + expect(protocol.readI32(), 30); // key + expect(protocol.readI32(), 40); // value + protocol.readMapEnd(); + + protocol.readMapEnd(); + protocol.readFieldEnd(); + protocol.readStructEnd(); + protocol.readMessageEnd(); + }); + + test('Test nested maps and lists', () async { + // {1: [{10: 20}], 2: [{30: 40}]} + protocol.writeMapBegin(new TMap(TType.I32, TType.LIST, 2)); + + protocol.writeI32(1); // key + protocol.writeListBegin(new TList(TType.MAP, 1)); + protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1)); + protocol.writeI32(10); // key + protocol.writeI32(20); // value + protocol.writeMapEnd(); + protocol.writeListEnd(); + + protocol.writeI32(2); // key + protocol.writeListBegin(new TList(TType.MAP, 1)); + protocol.writeMapBegin(new TMap(TType.I32, TType.I32, 1)); + protocol.writeI32(30); // key + protocol.writeI32(40); // value + protocol.writeMapEnd(); + protocol.writeListEnd(); + + protocol.writeMapEnd(); + protocol.writeMessageEnd(); + + await protocol.transport.flush(); + + protocol.readMessageBegin(); + expect(protocol.readMapBegin().length, 2); + + expect(protocol.readI32(), 1); // key + expect(protocol.readListBegin().length, 1); + expect(protocol.readMapBegin().length, 1); + expect(protocol.readI32(), 10); // key + expect(protocol.readI32(), 20); // value + protocol.readMapEnd(); + protocol.readListEnd(); + + expect(protocol.readI32(), 2); // key + expect(protocol.readListBegin().length, 1); + expect(protocol.readMapBegin().length, 1); + expect(protocol.readI32(), 30); // key + expect(protocol.readI32(), 40); // value + protocol.readMapEnd(); + protocol.readListEnd(); + + protocol.readMapEnd(); + protocol.readMessageEnd(); + }); + }; + + group('JSON', () { + setUp(() { + protocol = new TJsonProtocol(new TBufferedTransport()); + protocol.writeMessageBegin(message); + }); + + group('shared tests', sharedTests); + }); + + group('binary', () { + setUp(() { + protocol = new TBinaryProtocol(new TBufferedTransport()); + protocol.writeMessageBegin(message); + }); + + group('shared tests', sharedTests); + }); +} + +class Primitive { + final Function read; + final Function write; + final defaultValue; + + Primitive(this.read, this.write, this.defaultValue); +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/test/t_application_error_test.dart ---------------------------------------------------------------------- diff --git a/lib/dart/test/t_application_error_test.dart b/lib/dart/test/t_application_error_test.dart new file mode 100644 index 0000000..511d8d6 --- /dev/null +++ b/lib/dart/test/t_application_error_test.dart @@ -0,0 +1,46 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +library thrift.test.t_application_error_test; + +import 'package:test/test.dart'; +import 'package:thrift/thrift.dart'; + +void main() { + TProtocol protocol; + + setUp(() { + protocol = new TBinaryProtocol(new TBufferedTransport()); + }); + + test('Write and read an application error', () { + var expectedType = TApplicationErrorType.INTERNAL_ERROR; + var expectedMessage = 'test error message'; + + TApplicationError error = + new TApplicationError(expectedType, expectedMessage); + error.write(protocol); + + protocol.transport.flush(); + + TApplicationError subject = TApplicationError.read(protocol); + + expect(subject, isNotNull); + expect(subject.type, expectedType); + expect(subject.message, expectedMessage); + }); +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/test/transport/t_http_transport_test.dart ---------------------------------------------------------------------- diff --git a/lib/dart/test/transport/t_http_transport_test.dart b/lib/dart/test/transport/t_http_transport_test.dart new file mode 100644 index 0000000..7fcab3e --- /dev/null +++ b/lib/dart/test/transport/t_http_transport_test.dart @@ -0,0 +1,165 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +library thrift.test.transport.t_socket_transport_test; + +import 'dart:async'; +import 'dart:convert' show Encoding; +import 'dart:convert' show Utf8Codec; +import 'dart:typed_data' show Uint8List; + +import 'package:crypto/crypto.dart' show CryptoUtils; +import 'package:http/http.dart' show BaseRequest; +import 'package:http/http.dart' show Client; +import 'package:http/http.dart' show Response; +import 'package:http/http.dart' show StreamedResponse; +import 'package:test/test.dart'; +import 'package:thrift/thrift.dart'; + +void main() { + const utf8Codec = const Utf8Codec(); + + group('THttpClientTransport', () { + FakeHttpClient client; + THttpClientTransport transport; + + setUp(() { + client = new FakeHttpClient(sync: false); + var config = new THttpConfig(Uri.parse('http://localhost'), {}); + transport = new THttpClientTransport(client, config); + }); + + test('Test transport sends body', () async { + var expectedText = 'my request'; + transport.writeAll(utf8Codec.encode(expectedText)); + + expect(client.postRequest, isEmpty); + + await transport.flush(); + + expect(client.postRequest, isNotEmpty); + + var requestText = + utf8Codec.decode(CryptoUtils.base64StringToBytes(client.postRequest)); + expect(requestText, expectedText); + }); + + test('Test transport receives response', () async { + var expectedText = 'my response'; + var expectedBytes = utf8Codec.encode(expectedText); + client.postResponse = CryptoUtils.bytesToBase64(expectedBytes); + + transport.writeAll(utf8Codec.encode('my request')); + expect(transport.hasReadData, isFalse); + + await transport.flush(); + + expect(transport.hasReadData, isTrue); + + var buffer = new Uint8List(expectedBytes.length); + transport.readAll(buffer, 0, expectedBytes.length); + + var bufferText = utf8Codec.decode(buffer); + expect(bufferText, expectedText); + }); + }); + + group('THttpClientTransport with multiple messages', () { + FakeHttpClient client; + THttpClientTransport transport; + + setUp(() { + client = new FakeHttpClient(sync: true); + var config = new THttpConfig(Uri.parse('http://localhost'), {}); + transport = new THttpClientTransport(client, config); + }); + + test('Test read correct buffer after flush', () async { + String bufferText; + var expectedText = 'response 1'; + var expectedBytes = utf8Codec.encode(expectedText); + + // prepare a response + transport.writeAll(utf8Codec.encode('request 1')); + client.postResponse = CryptoUtils.bytesToBase64(expectedBytes); + + Future responseReady = transport.flush().then((_) { + var buffer = new Uint8List(expectedBytes.length); + transport.readAll(buffer, 0, expectedBytes.length); + bufferText = utf8Codec.decode(buffer); + }); + + // prepare a second response + transport.writeAll(utf8Codec.encode('request 2')); + var response2Bytes = utf8Codec.encode('response 2'); + client.postResponse = CryptoUtils.bytesToBase64(response2Bytes); + await transport.flush(); + + await responseReady; + expect(bufferText, expectedText); + }); + }); +} + +class FakeHttpClient implements Client { + String postResponse = ''; + String postRequest = ''; + + final bool sync; + + FakeHttpClient({this.sync: false}); + + Future<Response> post(url, + {Map<String, String> headers, body, Encoding encoding}) { + postRequest = body; + var response = new Response(postResponse, 200); + + if (sync) { + return new Future.sync(() => response); + } else { + return new Future.value(response); + } + } + + Future<Response> head(url, {Map<String, String> headers}) => + throw new UnimplementedError(); + + Future<Response> get(url, {Map<String, String> headers}) => + throw new UnimplementedError(); + + Future<Response> put(url, + {Map<String, String> headers, body, Encoding encoding}) => + throw new UnimplementedError(); + + Future<Response> patch(url, + {Map<String, String> headers, body, Encoding encoding}) => + throw new UnimplementedError(); + + Future<Response> delete(url, {Map<String, String> headers}) => + throw new UnimplementedError(); + + Future<String> read(url, {Map<String, String> headers}) => + throw new UnimplementedError(); + + Future<Uint8List> readBytes(url, {Map<String, String> headers}) => + throw new UnimplementedError(); + + Future<StreamedResponse> send(BaseRequest request) => + throw new UnimplementedError(); + + void close() => throw new UnimplementedError(); +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/test/transport/t_socket_transport_test.dart ---------------------------------------------------------------------- diff --git a/lib/dart/test/transport/t_socket_transport_test.dart b/lib/dart/test/transport/t_socket_transport_test.dart new file mode 100644 index 0000000..997df0d --- /dev/null +++ b/lib/dart/test/transport/t_socket_transport_test.dart @@ -0,0 +1,312 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +library thrift.test.transport.t_socket_transport_test; + +import 'dart:async'; +import 'dart:convert' show Utf8Codec; +import 'dart:typed_data' show Uint8List; + +import 'package:crypto/crypto.dart' show CryptoUtils; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; +import 'package:thrift/thrift.dart'; + +void main() { + const utf8Codec = const Utf8Codec(); + + final requestText = 'my test request'; + final requestBytes = new Uint8List.fromList(utf8Codec.encode(requestText)); + final requestBase64 = CryptoUtils.bytesToBase64(requestBytes); + + final responseText = 'response 1'; + final responseBytes = new Uint8List.fromList(utf8Codec.encode(responseText)); + final responseBase64 = CryptoUtils.bytesToBase64(responseBytes); + + final framedResponseBase64 = + CryptoUtils.bytesToBase64(_getFramedResponse(responseBytes)); + + group('TClientSocketTransport', () { + FakeSocket socket; + TTransport transport; + + setUp(() async { + socket = new FakeSocket(sync: false); + await socket.open(); + transport = new TClientSocketTransport(socket); + await transport.open(); + transport.writeAll(requestBytes); + }); + + test('Test client sending data over transport', () async { + expect(socket.sendPayload, isNull); + + Future responseReady = transport.flush(); + + // allow microtask events to finish + await new Future.value(); + + expect(socket.sendPayload, isNotNull); + expect(socket.sendPayload, requestBytes); + + // simulate a response + socket.receiveFakeMessage(responseBase64); + + await responseReady; + var buffer = new Uint8List(responseBytes.length); + transport.readAll(buffer, 0, responseBytes.length); + var bufferText = utf8Codec.decode(buffer); + + expect(bufferText, responseText); + }); + }, timeout: new Timeout(new Duration(seconds: 1))); + + group('TClientSocketTransport with FramedTransport', () { + FakeSocket socket; + TTransport transport; + + setUp(() async { + socket = new FakeSocket(sync: true); + await socket.open(); + + transport = new TFramedTransport(new TClientSocketTransport(socket)); + await transport.open(); + transport.writeAll(requestBytes); + }); + + test('Test client sending data over framed transport', () async { + String bufferText; + + Future responseReady = transport.flush().then((_) { + var buffer = new Uint8List(responseBytes.length); + transport.readAll(buffer, 0, responseBytes.length); + bufferText = utf8Codec.decode(buffer); + }); + + // simulate a response + socket.receiveFakeMessage(framedResponseBase64); + + await responseReady; + expect(bufferText, responseText); + }); + }, timeout: new Timeout(new Duration(seconds: 1))); + + group('TAsyncClientSocketTransport', () { + FakeSocket socket; + FakeProtocolFactory protocolFactory; + TTransport transport; + + setUp(() async { + socket = new FakeSocket(sync: true); + await socket.open(); + + protocolFactory = new FakeProtocolFactory(); + protocolFactory.message = new TMessage('foo', TMessageType.CALL, 123); + transport = new TAsyncClientSocketTransport( + socket, new TMessageReader(protocolFactory), + responseTimeout: Duration.ZERO); + await transport.open(); + transport.writeAll(requestBytes); + }); + + test('Test response correlates to correct request', () async { + String bufferText; + + Future responseReady = transport.flush().then((_) { + var buffer = new Uint8List(responseBytes.length); + transport.readAll(buffer, 0, responseBytes.length); + bufferText = utf8Codec.decode(buffer); + }); + + // simulate a response + protocolFactory.message = new TMessage('foo', TMessageType.REPLY, 123); + socket.receiveFakeMessage(responseBase64); + + // simulate a second response + var response2Text = 'response 2'; + var response2Bytes = + new Uint8List.fromList(utf8Codec.encode(response2Text)); + var response2Base64 = CryptoUtils.bytesToBase64(response2Bytes); + protocolFactory.message = new TMessage('foo2', TMessageType.REPLY, 124); + socket.receiveFakeMessage(response2Base64); + + await responseReady; + expect(bufferText, responseText); + }); + + test('Test response timeout', () async { + Future responseReady = transport.flush(); + expect(responseReady, throwsA(new isInstanceOf<TimeoutException>())); + }); + }, timeout: new Timeout(new Duration(seconds: 1))); + + group('TAsyncClientSocketTransport with TFramedTransport', () { + FakeSocket socket; + FakeProtocolFactory protocolFactory; + TTransport transport; + + setUp(() async { + socket = new FakeSocket(sync: true); + await socket.open(); + + protocolFactory = new FakeProtocolFactory(); + protocolFactory.message = new TMessage('foo', TMessageType.CALL, 123); + var messageReader = new TMessageReader(protocolFactory, + byteOffset: TFramedTransport.headerByteCount); + + transport = new TFramedTransport(new TAsyncClientSocketTransport( + socket, messageReader, + responseTimeout: Duration.ZERO)); + await transport.open(); + transport.writeAll(requestBytes); + }); + + test('Test async client sending data over framed transport', () async { + String bufferText; + + Future responseReady = transport.flush().then((_) { + var buffer = new Uint8List(responseBytes.length); + transport.readAll(buffer, 0, responseBytes.length); + bufferText = utf8Codec.decode(buffer); + }); + + // simulate a response + protocolFactory.message = new TMessage('foo', TMessageType.REPLY, 123); + socket.receiveFakeMessage(framedResponseBase64); + + await responseReady; + expect(bufferText, responseText); + }); + }, timeout: new Timeout(new Duration(seconds: 1))); + + group('TServerTransport', () { + test('Test server transport listens to socket', () async { + var socket = new FakeSocket(); + await socket.open(); + expect(socket.isOpen, isTrue); + + var transport = new TServerSocketTransport(socket); + expect(transport.hasReadData, isFalse); + + socket.receiveFakeMessage(requestBase64); + + // allow microtask events to finish + await new Future.value(); + + expect(transport.hasReadData, isTrue); + + var buffer = new Uint8List(requestBytes.length); + transport.readAll(buffer, 0, requestBytes.length); + + var bufferText = utf8Codec.decode(buffer); + expect(bufferText, requestText); + }); + + test('Test server sending data over transport', () async { + var socket = new FakeSocket(); + await socket.open(); + + var transport = new TServerSocketTransport(socket); + + transport.writeAll(responseBytes); + expect(socket.sendPayload, isNull); + + transport.flush(); + + // allow microtask events to finish + await new Future.value(); + + expect(socket.sendPayload, isNotNull); + expect(socket.sendPayload, responseBytes); + }); + }, timeout: new Timeout(new Duration(seconds: 1))); +} + +class FakeSocket extends TSocket { + final StreamController<TSocketState> _onStateController; + Stream<TSocketState> get onState => _onStateController.stream; + + final StreamController<Object> _onErrorController; + Stream<Object> get onError => _onErrorController.stream; + + final StreamController<Uint8List> _onMessageController; + Stream<Uint8List> get onMessage => _onMessageController.stream; + + FakeSocket({bool sync: false}) + : _onStateController = new StreamController.broadcast(sync: sync), + _onErrorController = new StreamController.broadcast(sync: sync), + _onMessageController = new StreamController.broadcast(sync: sync); + + bool _isOpen; + + bool get isOpen => _isOpen; + + bool get isClosed => !isOpen; + + Future open() async { + _isOpen = true; + _onStateController.add(TSocketState.OPEN); + } + + Future close() async { + _isOpen = false; + _onStateController.add(TSocketState.CLOSED); + } + + Uint8List _sendPayload; + Uint8List get sendPayload => _sendPayload; + + void send(Uint8List data) { + if (!isOpen) throw new StateError('The socket is not open'); + + _sendPayload = data; + } + + void receiveFakeMessage(String base64) { + if (!isOpen) throw new StateError('The socket is not open'); + + var message = + new Uint8List.fromList(CryptoUtils.base64StringToBytes(base64)); + _onMessageController.add(message); + } +} + +class FakeProtocolFactory implements TProtocolFactory { + FakeProtocolFactory(); + + TMessage message; + + getProtocol(TTransport transport) => new FakeProtocol(message); +} + +class FakeProtocol extends Mock implements TProtocol { + FakeProtocol(this._message); + + TMessage _message; + + readMessageBegin() => _message; +} + +Uint8List _getFramedResponse(Uint8List responseBytes) { + var byteOffset = TFramedTransport.headerByteCount; + var response = new Uint8List(byteOffset + responseBytes.length); + + response.buffer.asByteData().setInt32(0, responseBytes.length); + response.setAll(byteOffset, responseBytes); + + return response; +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/test/transport/t_transport_test.dart ---------------------------------------------------------------------- diff --git a/lib/dart/test/transport/t_transport_test.dart b/lib/dart/test/transport/t_transport_test.dart new file mode 100644 index 0000000..0bb381a --- /dev/null +++ b/lib/dart/test/transport/t_transport_test.dart @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +library thrift.test.transport.t_socket_transport_test; + +import 'package:test/test.dart'; +import 'package:thrift/thrift.dart'; + +/// Common transport tests +void main() { + group('TTransportFactory', () { + test('transport is returned from base factory', () async { + TTransport result; + TTransport transport = null; + + var factory = new TTransportFactory(); + + result = await factory.getTransport(transport); + expect(result, isNull); + + transport = new TBufferedTransport(); + result = await factory.getTransport(transport); + + expect(result, transport); + }); + }); +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/lib/dart/tool/dev.dart ---------------------------------------------------------------------- diff --git a/lib/dart/tool/dev.dart b/lib/dart/tool/dev.dart new file mode 100644 index 0000000..27f8b8f --- /dev/null +++ b/lib/dart/tool/dev.dart @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +library tool.dev; + +import 'package:dart_dev/dart_dev.dart' show dev, config; + +main(List<String> args) async { + // https://github.com/Workiva/dart_dev + + var directories = ['lib/', 'test/', 'tool/']; + config.analyze.entryPoints = directories; + config.format.directories = directories; + config.copyLicense + ..licensePath = 'LICENSE_HEADER' + ..directories = directories; + + await dev(args); +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/test/Makefile.am ---------------------------------------------------------------------- diff --git a/test/Makefile.am b/test/Makefile.am index 6ebcd27..7590921 100755 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -40,6 +40,11 @@ SUBDIRS += php PRECROSS_TARGET += precross-php endif +if WITH_DART +SUBDIRS += dart +PRECROSS_TARGET += precross-dart +endif + if WITH_PYTHON SUBDIRS += py PRECROSS_TARGET += precross-py http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/test/dart/Makefile.am ---------------------------------------------------------------------- diff --git a/test/dart/Makefile.am b/test/dart/Makefile.am new file mode 100644 index 0000000..59b3b7d --- /dev/null +++ b/test/dart/Makefile.am @@ -0,0 +1,41 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-dart/thrift_test/lib/thrift_test.dart: ../ThriftTest.thrift + $(THRIFT) --gen dart ../ThriftTest.thrift + +pub-get-gen: gen-dart/thrift_test/lib/thrift_test.dart + cd gen-dart/thrift_test; ${DARTPUB} get + +pub-get: pub-get-gen + cd test_client; ${DARTPUB} get + +stubs: gen-dart/thrift_test/lib/thrift_test.dart pub-get + +precross: stubs + +check: stubs + +clean-local: + $(RM) -r gen-dart test_client/.pub + +client: stubs + ${DART} test_client/bin/main.dart http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/test/dart/test_client/bin/main.dart ---------------------------------------------------------------------- diff --git a/test/dart/test_client/bin/main.dart b/test/dart/test_client/bin/main.dart new file mode 100644 index 0000000..3733a08 --- /dev/null +++ b/test/dart/test_client/bin/main.dart @@ -0,0 +1,288 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// 'License'); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:collection/equality.dart'; +import 'package:thrift/thrift.dart'; +import 'package:thrift/thrift_console.dart'; +import 'package:thrift_test/thrift_test.dart'; + +ThriftTestClient client; +bool verbose; + +/// Adapted from TestClient.php +main(List<String> args) async { + var parser = new ArgParser(); + parser.addOption('host', defaultsTo: 'localhost', help: 'The server host'); + parser.addOption('port', defaultsTo: '9090', help: 'The port to connect to'); + parser.addOption('transport', + defaultsTo: 'buffered', + allowed: ['buffered', 'framed'], + help: 'The transport name', + allowedHelp: { + 'buffered': 'TBufferedTransport', + 'framed': 'TFramedTransport' + }); + parser.addOption('protocol', + defaultsTo: 'binary', + allowed: ['binary', 'json'], + help: 'The protocol name', + allowedHelp: {'binary': 'TBinaryProtocol', 'json': 'TJsonProtocol'}); + parser.addFlag('verbose', defaultsTo: false); + + ArgResults results; + try { + results = parser.parse(args); + } catch (e) { + stdout.writeln('$e\n'); + results = null; + } + + if (results == null) { + print(parser.usage); + exit(0); + } + + verbose = results['verbose'] == true; + + await init( + host: results['host'], + port: int.parse(results['port']), + transportType: results['transport'], + protocolType: results['protocol']).then((_) { + exit(0); + }).catchError((e) { + stdout.writeln('Error:'); + stdout.writeln('$e'); + if (e is Error) { + stdout.writeln('${e.stackTrace}'); + } + exit(1); + }); + exit(0); +} + +TProtocolFactory getProtocolFactory(String protocolType) { + if (protocolType == 'binary') { + return new TBinaryProtocolFactory(); + } else if (protocolType == 'json') { + return new TJsonProtocolFactory(); + } + + throw new ArgumentError.value(protocolType); +} + +Future init( + {String host, int port, String transportType, String protocolType}) async { + TTransport transport; + var protocolFactory = getProtocolFactory(protocolType); + + if (transportType == 'http') { + var httpClient = new HttpClient(); + var config = new THttpConfig(Uri.parse('http://localhost'), {}); + transport = new THttpClientTransport(httpClient, config); + } else { + var socket = await Socket.connect(host, port); + transport = new TClientSocketTransport(new TTcpSocket(socket)); + if (transportType == 'framed') { + transport = new TFramedTransport(transport); + } + } + + var protocol = protocolFactory.getProtocol(transport); + client = new ThriftTestClient(protocol); + + await transport.open(); + + await runTests(); +} + +Future _test(String name, Function testFunc) async { + if (verbose) stdout.write('$name... '); + await testFunc(); + if (verbose) stdout.writeln('success!'); +} + +Future runTests() async { + var xtruct = new Xtruct() + ..string_thing = 'Zero' + ..byte_thing = 1 + ..i32_thing = -3 + ..i64_thing = -5; + + await _test('testVoid', () async { + await client.testVoid(); + }); + + await _test('testString', () async { + var input = 'Test'; + var result = await client.testString(input); + if (result != input) throw new StateError('$result != $input'); + }); + + await _test('testBool', () async { + var input = true; + var result = await client.testBool(input); + if (result != input) throw new StateError('$result != $input'); + }); + + await _test('testByte', () async { + var input = 64; + var result = await client.testByte(input); + if (result != input) throw new StateError('$result != $input'); + }); + + await _test('testI32', () async { + var input = 2147483647; + var result = await client.testI32(input); + if (result != input) throw new StateError('$result != $input'); + }); + + await _test('testI64', () async { + var input = 9223372036854775807; + var result = await client.testI64(input); + if (result != input) throw new StateError('$result != $input'); + }); + + await _test('testDouble', () async { + var input = 3.1415926; + var result = await client.testDouble(input); + if (result != input) throw new StateError('$result != $input'); + }); + + await _test('testBinary', () async { + var utf8Codec = const Utf8Codec(); + var input = utf8Codec.encode('foo'); + var result = await client.testBinary(input); + var equality = const ListEquality(); + if (!equality.equals( + result, input)) throw new StateError('$result != $input'); + }); + + await _test('testStruct', () async { + var result = await client.testStruct(xtruct); + if ('$result' != '$xtruct') throw new StateError('$result != $xtruct'); + }); + + await _test('testNest', () async { + var input = new Xtruct2() + ..byte_thing = 1 + ..struct_thing = xtruct + ..i32_thing = -3; + + var result = await client.testNest(input); + if ('$result' != '$input') throw new StateError('$result != $input'); + }); + + await _test('testMap', () async { + Map<int, int> input = {1: -10, 2: -9, 3: -8, 4: -7, 5: -6}; + + var result = await client.testMap(input); + var equality = const MapEquality(); + if (!equality.equals( + result, input)) throw new StateError('$result != $input'); + }); + + await _test('testSet', () async { + var input = new Set.from([-2, -1, 0, 1, 2]); + var result = await client.testSet(input); + var equality = const SetEquality(); + if (!equality.equals( + result, input)) throw new StateError('$result != $input'); + }); + + await _test('testList', () async { + var input = [-2, -1, 0, 1, 2]; + var result = await client.testList(input); + var equality = const ListEquality(); + if (!equality.equals( + result, input)) throw new StateError('$result != $input'); + }); + + await _test('testEnum', () async { + await _testEnum(Numberz.ONE); + await _testEnum(Numberz.TWO); + await _testEnum(Numberz.THREE); + await _testEnum(Numberz.FIVE); + await _testEnum(Numberz.EIGHT); + }); + + await _test('testTypedef', () async { + var input = 309858235082523; + var result = await client.testTypedef(input); + if (result != input) throw new StateError('$result != $input'); + }); + + await _test('testMapMap', () async { + Map<int, Map<int, int>> result = await client.testMapMap(1); + if (result.isEmpty || + result[result.keys.first].isEmpty) throw new StateError( + 'expected Map<int, Map<int, int>> got $result'); + }); + + await _test('testInsanity', () async { + var input = new Insanity(); + input.userMap = {Numberz.FIVE: 5000}; + input.xtructs = [xtruct]; + + Map<int, Map<int, Insanity>> result = await client.testInsanity(input); + if (result.isEmpty || + result[result.keys.first].isEmpty) throw new StateError( + 'expected Map<int, Map<int, Insanity>> got $result'); + }); + + await _test('testMulti', () async { + var input = new Xtruct() + ..string_thing = 'Hello2' + ..byte_thing = 123 + ..i32_thing = 456 + ..i64_thing = 789; + + var result = await client.testMulti(input.byte_thing, input.i32_thing, + input.i64_thing, {1: 'one'}, Numberz.EIGHT, 5678); + if ('$result' != '$input') throw new StateError('$result != $input'); + }); + + await _test('testException', () async { + try { + await client.testException('Xception'); + } on Xception catch (x) { + return; + } + + throw new StateError('expected an Xception'); + }); + + await _test('testMultiException', () async { + try { + await client.testMultiException('Xception2', 'foo'); + } on Xception2 catch (x) { + return; + } + + throw new StateError('expected an Xception2'); + }); +} + +Future _testEnum(int input) async { + var result = await client.testEnum(input); + if (result != input) throw new StateError('$result != $input'); +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/test/dart/test_client/pubspec.yaml ---------------------------------------------------------------------- diff --git a/test/dart/test_client/pubspec.yaml b/test/dart/test_client/pubspec.yaml new file mode 100644 index 0000000..54d8b0f --- /dev/null +++ b/test/dart/test_client/pubspec.yaml @@ -0,0 +1,32 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# 'License'); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: thrift_test_client +version: 1.0.0-dev +description: A client integration test for the Dart Thrift library +author: Mark Erickson <[email protected]> +homepage: https://github.com/apache/thrift +environment: + sdk: ^1.12.0 +dependencies: + args: ^0.13.0 + thrift: + path: ../../../lib/dart + thrift_test: + path: ../gen-dart/thrift_test +dev_dependencies: + test: "^0.12.0" http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/test/tests.json ---------------------------------------------------------------------- diff --git a/test/tests.json b/test/tests.json index c902cd4..8c152fc 100644 --- a/test/tests.json +++ b/test/tests.json @@ -354,5 +354,27 @@ ] }, "workdir": "php" + }, + { + "name": "dart", + "client": { + "transports": [ + "buffered", + "framed", + "http" + ], + "sockets": [ + "ip" + ], + "protocols": [ + "binary", + "json" + ], + "command": [ + "dart", + "test_client/bin/main.dart" + ] + }, + "workdir": "dart" } ] http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/Makefile.am ---------------------------------------------------------------------- diff --git a/tutorial/Makefile.am b/tutorial/Makefile.am index 47711a9..37addda 100755 --- a/tutorial/Makefile.am +++ b/tutorial/Makefile.am @@ -62,6 +62,10 @@ if WITH_NODEJS SUBDIRS += nodejs endif +if WITH_DART +SUBDIRS += dart +endif + # # generate html for ThriftTest.thrift # http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/Makefile.am ---------------------------------------------------------------------- diff --git a/tutorial/dart/Makefile.am b/tutorial/dart/Makefile.am new file mode 100644 index 0000000..2bb6bf2 --- /dev/null +++ b/tutorial/dart/Makefile.am @@ -0,0 +1,56 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +THRIFT = $(top_builddir)/compiler/cpp/thrift + +gen-dart/tutorial/lib/tutorial.dart gen-dart/shared/lib/shared.dart: $(top_srcdir)/tutorial/tutorial.thrift + $(THRIFT) --gen dart -r $< + +all-local: gen-dart/tutorial/lib/tutorial.dart pub-get + +clean-local: + $(RM) -r gen-* + +pub-get: pub-get-gen pub-get-client pub-get-console-client pub-get-server + +pub-get-gen: pub-get-tutorial pub-get-shared + +pub-get-tutorial: gen-dart/tutorial/lib/tutorial.dart + cd gen-dart/tutorial; ${DARTPUB} get + +pub-get-shared: gen-dart/shared/lib/shared.dart + cd gen-dart/shared; ${DARTPUB} get + +pub-get-client: + cd client; ${DARTPUB} get + +pub-get-console-client: + cd console_client; ${DARTPUB} get + +pub-get-server: + cd client; ${DARTPUB} get + +tutorialserver: pub-get-gen pub-get-server + ${DART} server/bin/main.dart + +tutorialclient: pub-get-gen pub-get-client + cd client; ${DARTPUB} serve + +tutorialconsoleclient: pub-get-console-client + ${DART} console-client/bin/main.dart http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/build.sh ---------------------------------------------------------------------- diff --git a/tutorial/dart/build.sh b/tutorial/dart/build.sh new file mode 100644 index 0000000..eabe04a --- /dev/null +++ b/tutorial/dart/build.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# 'License'); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -e; +rm -r gen-dart || true; + +thrift --gen dart ../shared.thrift; +cd gen-dart/shared; +pub get; +cd ../..; + +thrift --gen dart ../tutorial.thrift; +cd gen-dart/tutorial; +pub get; +cd ../..; + +cd client; +pub get; +cd ..; + +cd console_client; +pub get; +cd ..; + +cd server; +pub get; +cd ..; + +dartfmt -w gen-dart; + +echo "\nEnjoy the Dart tutorial!"; +echo "\nTo run the server:"; +echo "> dart server/bin/main.dart"; +echo "\nTo run the client:"; +echo "# Serve the app from the client directory and view in a browser"; +echo "> cd client;"; +echo "> pub serve;"; +echo "\nTo run the console client:"; +echo "> dart console_client/bin/main.dart"; +echo ""; http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/client/pubspec.yaml ---------------------------------------------------------------------- diff --git a/tutorial/dart/client/pubspec.yaml b/tutorial/dart/client/pubspec.yaml new file mode 100644 index 0000000..97c625b --- /dev/null +++ b/tutorial/dart/client/pubspec.yaml @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# 'License'); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: tutorial_client +version: 1.0.0-dev +description: A Dart client implementation of the Apache Thrift tutorial +author: Mark Erickson <[email protected]> +homepage: https://github.com/apache/thrift + +environment: + sdk: ^1.12.0 + +dependencies: + browser: ^0.10.0 + shared: + path: ../gen-dart/shared + thrift: + path: ../../../lib/dart + tutorial: + path: ../gen-dart/tutorial http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/client/web/client.dart ---------------------------------------------------------------------- diff --git a/tutorial/dart/client/web/client.dart b/tutorial/dart/client/web/client.dart new file mode 100644 index 0000000..4f02d0d --- /dev/null +++ b/tutorial/dart/client/web/client.dart @@ -0,0 +1,278 @@ +/// Licensed to the Apache Software Foundation (ASF) under one +/// or more contributor license agreements. See the NOTICE file +/// distributed with this work for additional information +/// regarding copyright ownership. The ASF licenses this file +/// to you under the Apache License, Version 2.0 (the +/// "License"); you may not use this file except in compliance +/// with the License. You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, +/// software distributed under the License is distributed on an +/// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +/// KIND, either express or implied. See the License for the +/// specific language governing permissions and limitations +/// under the License. + +import 'dart:html'; + +import 'package:thrift/thrift.dart'; +import 'package:thrift/thrift_browser.dart'; +import 'package:shared/shared.dart'; +import 'package:tutorial/tutorial.dart'; + +/// Adapted from the AS3 tutorial +void main() { + new CalculatorUI(querySelector('#output')).start(); +} + +class CalculatorUI { + final DivElement output; + + CalculatorUI(this.output); + + TTransport _transport; + Calculator _calculatorClient; + + void start() { + _buildInterface(); + _initConnection(); + } + + void _validate() { + if (!_transport.isOpen) { + window.alert("The transport is not open!"); + } + } + + void _initConnection() { + _transport = new TAsyncClientSocketTransport( + new TWebSocket(Uri.parse('ws://127.0.0.1:9090/ws')), + new TMessageReader(new TBinaryProtocolFactory())); + TProtocol protocol = new TBinaryProtocol(_transport); + _transport.open(); + + _calculatorClient = new CalculatorClient(protocol); + } + + void _buildInterface() { + output.children.forEach((e) { + e.remove(); + }); + + _buildPingComponent(); + + _buildAddComponent(); + + _buildCalculatorComponent(); + + _buildGetStructComponent(); + } + + void _buildPingComponent() { + output.append(new HeadingElement.h3()..text = "Ping"); + ButtonElement pingButton = new ButtonElement() + ..text = "PING" + ..onClick.listen(_onPingClick); + output.append(pingButton); + } + + void _onPingClick(MouseEvent e) { + _validate(); + + _calculatorClient.ping(); + } + + void _buildAddComponent() { + output.append(new HeadingElement.h3()..text = "Add"); + InputElement num1 = new InputElement() + ..id = "add1" + ..type = "number" + ..style.fontSize = "14px" + ..style.width = "50px"; + output.append(num1); + SpanElement op = new SpanElement() + ..text = "+" + ..style.fontSize = "14px" + ..style.marginLeft = "10px"; + output.append(op); + InputElement num2 = new InputElement() + ..id = "add2" + ..type = "number" + ..style.fontSize = "14px" + ..style.width = "50px" + ..style.marginLeft = "10px"; + output.append(num2); + ButtonElement addButton = new ButtonElement() + ..text = "=" + ..style.fontSize = "14px" + ..style.marginLeft = "10px" + ..onClick.listen(_onAddClick); + output.append(addButton); + SpanElement result = new SpanElement() + ..id = "addResult" + ..style.fontSize = "14px" + ..style.marginLeft = "10px"; + output.append(result); + } + + void _onAddClick(MouseEvent e) { + _validate(); + + InputElement num1 = querySelector("#add1"); + InputElement num2 = querySelector("#add2"); + SpanElement result = querySelector("#addResult"); + + _calculatorClient + .add(int.parse(num1.value), int.parse(num2.value)) + .then((int n) { + result.text = "$n"; + }); + } + + void _buildCalculatorComponent() { + output.append(new HeadingElement.h3()..text = "Calculator"); + InputElement num1 = new InputElement() + ..id = "calc1" + ..type = "number" + ..style.fontSize = "14px" + ..style.width = "50px"; + output.append(num1); + SelectElement op = new SelectElement() + ..id = "calcOp" + ..multiple = false + ..selectedIndex = 0 + ..style.fontSize = "16px" + ..style.marginLeft = "10px" + ..style.width = "50px"; + OptionElement addOp = new OptionElement() + ..text = "+" + ..value = Operation.ADD.toString(); + op.add(addOp, 0); + OptionElement subtractOp = new OptionElement() + ..text = "-" + ..value = Operation.SUBTRACT.toString(); + op.add(subtractOp, 1); + OptionElement multiplyOp = new OptionElement() + ..text = "*" + ..value = Operation.MULTIPLY.toString(); + op.add(multiplyOp, 2); + OptionElement divideOp = new OptionElement() + ..text = "/" + ..value = Operation.DIVIDE.toString(); + op.add(divideOp, 3); + output.append(op); + InputElement num2 = new InputElement() + ..id = "calc2" + ..type = "number" + ..style.fontSize = "14px" + ..style.width = "50px" + ..style.marginLeft = "10px"; + output.append(num2); + ButtonElement calcButton = new ButtonElement() + ..text = "=" + ..style.fontSize = "14px" + ..style.marginLeft = "10px" + ..onClick.listen(_onCalcClick); + output.append(calcButton); + SpanElement result = new SpanElement() + ..id = "calcResult" + ..style.fontSize = "14px" + ..style.marginLeft = "10px"; + output.append(result); + output.append(new BRElement()); + output.append(new BRElement()); + LabelElement logIdLabel = new LabelElement() + ..text = "Log ID:" + ..style.fontSize = "14px"; + output.append(logIdLabel); + InputElement logId = new InputElement() + ..id = "logId" + ..type = "number" + ..value = "1" + ..style.fontSize = "14px" + ..style.width = "50px" + ..style.marginLeft = "10px"; + output.append(logId); + LabelElement commentLabel = new LabelElement() + ..text = "Comment:" + ..style.fontSize = "14px" + ..style.marginLeft = "10px"; + output.append(commentLabel); + InputElement comment = new InputElement() + ..id = "comment" + ..style.fontSize = "14px" + ..style.width = "100px" + ..style.marginLeft = "10px"; + output.append(comment); + } + + void _onCalcClick(MouseEvent e) { + _validate(); + + InputElement num1 = querySelector("#calc1"); + InputElement num2 = querySelector("#calc2"); + SelectElement op = querySelector("#calcOp"); + SpanElement result = querySelector("#calcResult"); + InputElement logId = querySelector("#logId"); + InputElement comment = querySelector("#comment"); + + int logIdValue = int.parse(logId.value); + logId.value = (logIdValue + 1).toString(); + + Work work = new Work(); + work.num1 = int.parse(num1.value); + work.num2 = int.parse(num2.value); + work.op = int.parse(op.options[op.selectedIndex].value); + work.comment = comment.value; + + _calculatorClient.calculate(logIdValue, work).then((int n) { + result.text = "$n"; + }); + } + + void _buildGetStructComponent() { + output.append(new HeadingElement.h3()..text = "Get Struct"); + LabelElement logIdLabel = new LabelElement() + ..text = "Struct Key:" + ..style.fontSize = "14px"; + output.append(logIdLabel); + InputElement logId = new InputElement() + ..id = "structKey" + ..type = "number" + ..value = "1" + ..style.fontSize = "14px" + ..style.width = "50px" + ..style.marginLeft = "10px"; + output.append(logId); + ButtonElement getStructButton = new ButtonElement() + ..text = "GET" + ..style.fontSize = "14px" + ..style.marginLeft = "10px" + ..onClick.listen(_onGetStructClick); + output.append(getStructButton); + output.append(new BRElement()); + output.append(new BRElement()); + TextAreaElement result = new TextAreaElement() + ..id = "getStructResult" + ..style.fontSize = "14px" + ..style.width = "300px" + ..style.height = "50px" + ..style.marginLeft = "10px"; + output.append(result); + } + + void _onGetStructClick(MouseEvent e) { + _validate(); + + InputElement structKey = querySelector("#structKey"); + TextAreaElement result = querySelector("#getStructResult"); + + _calculatorClient + .getStruct(int.parse(structKey.value)) + .then((SharedStruct s) { + result.text = "${s.toString()}"; + }); + } +} http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/client/web/index.html ---------------------------------------------------------------------- diff --git a/tutorial/dart/client/web/index.html b/tutorial/dart/client/web/index.html new file mode 100644 index 0000000..64b184e --- /dev/null +++ b/tutorial/dart/client/web/index.html @@ -0,0 +1,36 @@ +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Thrift Tutorial</title> + <link rel="stylesheet" href="styles.css"> + <script async src="client.dart" type="application/dart"></script> + <script async src="packages/browser/dart.js"></script> +</head> + +<body> + + <div id="output"></div> + +</body> +</html> http://git-wip-us.apache.org/repos/asf/thrift/blob/932c4700/tutorial/dart/client/web/styles.css ---------------------------------------------------------------------- diff --git a/tutorial/dart/client/web/styles.css b/tutorial/dart/client/web/styles.css new file mode 100644 index 0000000..c031502 --- /dev/null +++ b/tutorial/dart/client/web/styles.css @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +@import url(https://fonts.googleapis.com/css?family=Roboto); + +html, body { + width: 100%; + height: 100%; + margin: 0; + padding: 10px; + font-family: 'Roboto', sans-serif; +} + +h3 { + border-bottom: solid; + border-width: thin; + padding-top: 20px; +}
