Skip to content

Commit

Permalink
Fixes #49 also better fixes #39 with proper ConvertPseudoTypes usage.
Browse files Browse the repository at this point in the history
  • Loading branch information
bchavez committed May 11, 2016
1 parent 3683f15 commit 90734af
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 74 deletions.
2 changes: 2 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* BREAKING: Issue 39 - Pseudo types are now converted by default in JToken types (JObject, JArray).
* You'll need to specify .Run*(conn, new { time_format: `raw` }) to keep raw types
* from being converted. Other raw types: binary_format and group_format.
* BREAKING: Issue 49 - Handle DateTime and DateTimeOffset with ReqlDateTimeConverter
* instead of Iso8601 AST term.

## v2.3.1-beta-1
* Compatibility with RethinkDB 2.3 and new user/pass authentication system.
Expand Down
2 changes: 1 addition & 1 deletion Source/RethinkDb.Driver.Tests/ReQL/BinaryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public void binary_echo()
[Test]
public void can_get_raw_binary_type()
{
JObject reqlType = R.binary(new byte[] { 1, 2, 3 }).Run<JObject>(conn);
JObject reqlType = R.binary(new byte[] { 1, 2, 3 }).Run<JObject>(conn, new {binary_format = "raw"});
reqlType[Converter.PseudoTypeKey].ToString().Should().Be("BINARY");
}

Expand Down
48 changes: 40 additions & 8 deletions Source/RethinkDb.Driver.Tests/ReQL/DateAndTimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,61 @@ public void datetime_expr_utctime()
}

[Test]
public void unspecified_date_time()
public void unspecified_date_time_should_come_back_local_by_default()
{
var date = new DateTime(2015, 11, 14, 1, 2, 3, DateTimeKind.Unspecified);

//ISO 8601 string has no time zone, and no default time zone was provided.
var result = R.Expr(date).RunResult<DateTime>(conn);

var expected = new DateTime(2015, 11, 14, 1, 2, 3, DateTimeKind.Local);

result.Should().BeCloseTo(expected, (int)TimeSpan.FromMinutes(30).TotalMilliseconds);
}

[Test]
public void should_be_able_to_use_datetime_with_iso8601()
{
var date = new DateTime(2015, 11, 14, 1, 2, 3, DateTimeKind.Unspecified);

//ISO 8601 string has no time zone, and no default time zone was provided.
Action action = () =>
{
DateTime result = R.Expr(date).Run<DateTime>(conn);
};
{
DateTime result = R.Iso8601(date).Run<DateTime>(conn);
};

action.ShouldThrow<ReqlQueryLogicError>("DateTime unspecified timezone should not be ISO8601 valid.");
}

[Test]
public void can_convert_utc_datetime_to_epochtime()
{
var date = new DateTime(2015, 11, 14, 1, 2, 3, DateTimeKind.Utc);

var result = R.EpochTime(date).RunResult<DateTime>(conn);

result.Should().BeCloseTo(date, (int)TimeSpan.FromMinutes(30).TotalMilliseconds);
}

[Test]
public void can_convert_local_datetime_to_epochtime()
{
var date = new DateTime(2015, 11, 14, 1, 2, 3, DateTimeKind.Local);

var result = R.EpochTime(date).RunResult<DateTime>(conn);

result.Should().BeCloseTo(date.ToUniversalTime(), (int)TimeSpan.FromMinutes(30).TotalMilliseconds);
}

[Test]
public void unspecified_date_time_with_default_timezone()
{
var date = new DateTime(2015, 11, 14, 1, 2, 3, DateTimeKind.Unspecified);

var datestr = date.ToString("o");

var default_timezone = new {default_timezone = "-09:00"};

DateTime result = (R.Expr(date) as Iso8601)[default_timezone].Run<DateTime>(conn);
DateTime result = R.Iso8601(datestr)[default_timezone].Run<DateTime>(conn);

var dateTimeUtc = new DateTime(2015, 11, 14, 1, 2, 3) + TimeSpan.FromHours(9);

Expand All @@ -81,13 +114,12 @@ public void unspecified_date_time_with_default_timezone()
DateTime result2 = R.Iso8601(withoutTimezone)[default_timezone].Run<DateTime>(conn);

result2.ToUniversalTime().Should().BeCloseTo(dateTimeUtc, 1);

}

[Test]
public void use_raw_object()
{
JObject result = R.Now().Run<JObject>(conn);
JObject result = R.Now().Run<JObject>(conn, new {time_format="raw"});
//ten minute limit for clock drift.

result["$reql_type$"].ToString().Should().Be("TIME");
Expand Down
47 changes: 47 additions & 0 deletions Source/RethinkDb.Driver.Tests/ReQL/GitHubIssues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,53 @@ public void issue_41_ensure_run_helpers_throw_error_first_before_direct_conversi
action.ShouldThrow<ReqlRuntimeError>();
}


public class Issue49
{
[JsonProperty("id")]
public int Id = 1;
public DateTime BigBang { get; set; }
}

[Test]
public void issue_49_use_reqldatetimeconverter_for_dates_in_ast()
{
ClearDefaultTable();

var mindate = DateTime.MinValue.ToUniversalTime();

var insertResult = R
.Db(DbName)
.Table(TableName)
.Insert(new Issue49 {BigBang = mindate})
.RunResult(conn);

insertResult.Errors.Should().Be(0);

var updateResult = R
.Db(DbName)
.Table(TableName)
.Get(1)
.Update(orig =>
{
var unchanged = orig["BigBang"].Eq(mindate);
return R.Error(unchanged.CoerceTo("string"));
})
.OptArg("return_changes", true)
.RunResult(conn);

updateResult.Errors.Should().Be(1);
updateResult.FirstError.Should().Be("true");

var filter =
R.Db(DbName)
.Table(TableName)
.Filter(x => x["BigBang"].Eq(mindate))
.RunResult<List<Issue49>>(conn);

filter.Count.Should().Be(1);
filter[0].BigBang.Should().Be(mindate);
}
}

}
46 changes: 46 additions & 0 deletions Source/RethinkDb.Driver/Ast/TopLevel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma warning disable 1591 // Missing XML comment for publicly visible type or member

using System;
using RethinkDb.Driver.Net.JsonConverters;

namespace RethinkDb.Driver.Ast
{
public partial class TopLevel
{
/// <summary>
/// Type-safe helper method for R.Iso8601
/// </summary>
public Iso8601 Iso8601(DateTime? datetime)
{
var str = datetime?.ToString("o");
return Ast.Iso8601.FromString(str);
}
/// <summary>
/// Type-safe helper method for R.Iso8601
/// </summary>
public Iso8601 Iso8601(DateTimeOffset? datetime)
{
var str = datetime?.ToString("o");
return Ast.Iso8601.FromString(str);
}

/// <summary>
/// Type-safe helper for R.EpochTime
/// </summary>
public EpochTime EpochTime(DateTime? datetime)
{
var ticks = datetime?.ToUniversalTime().Ticks;
var epoch = ReqlDateTimeConverter.ToUnixTime(ticks.Value);
return EpochTime(epoch);
}
/// <summary>
/// Type-safe helper for R.EpochTime
/// </summary>
public EpochTime EpochTime(DateTimeOffset? datetime)
{
var ticks = datetime?.UtcTicks;
var epoch = ReqlDateTimeConverter.ToUnixTime(ticks.Value);
return EpochTime(epoch);
}
}
}
44 changes: 6 additions & 38 deletions Source/RethinkDb.Driver/Ast/Util.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,38 +39,10 @@ private static ReqlAst ToReqlAst(object val, int remainingDepth)
return ast;
}

//GitHub Issue #21 - Request - Allow Inserting JObject
var token = val as JToken;
if( token != null )
{
//First way to do it is to convert the whole thing into JSON and have
//the server parse it. One thing we'd need to watch out for is
//the DateTime conversion. ReqlDateTimeConverter would need to change
//so that it directly serializes to $reql_type$:TIME instead of an AST
//Iso8601(string) with [49,...]. It's probably more proper to serialize
//to $reql_type$ than a ReQL Iso8601(string)[49, ...].
//var json = token.ToString(Formatting.None, Converter.Serializer.Converters.ToArray());
//return new Json(json);
return new Poco(token);

//Another way to do it: De-construct the JObject like we do to an IDictionary...
//Pro: More complete
//Con: Easy to get wrong, lots more code, code-duplication with IDictionary
//
//if (token.Type == JTokenType.Object)
//{
// var jobj = val as IDictionary<string, JToken>;
// var obj = new Dictionary<string, ReqlAst>();
// foreach (var t in jobj)
// {
// obj[t.Key] = ToReqlAst(t.Value);
// }
// return MakeObj.fromMap(obj);
//}
//else if (token.Type == JTokenType.Bytes) .. and for each supported JSON native type.
//{
//
//}
}

var lst = val as IList;
Expand Down Expand Up @@ -107,21 +79,17 @@ private static ReqlAst ToReqlAst(object val, int remainingDepth)
return Func.FromLambda(del);
}


if( val is DateTime )
var dt = val as DateTime?;
if (dt != null)
{
var dt = (DateTime)val;
var isoStr = dt.ToString("o");
return Iso8601.FromString(isoStr);
return new Poco(dt);
}
if( val is DateTimeOffset )
var dto = val as DateTimeOffset?;
if (dto != null)
{
var dt = (DateTimeOffset)val;
var isoStr = dt.ToString("o");
return Iso8601.FromString(isoStr);
return new Poco(dto);
}


var @int = val as int?;
if( @int != null )
{
Expand Down
2 changes: 1 addition & 1 deletion Source/RethinkDb.Driver/Generated/Model/TopLevel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
using RethinkDb.Driver.Ast;

namespace RethinkDb.Driver.Ast {
public class TopLevel {
public partial class TopLevel {

public ReqlExpr Expr(Object value){
return Util.ToReqlExpr(value);
Expand Down
12 changes: 8 additions & 4 deletions Source/RethinkDb.Driver/Net/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,8 @@ protected virtual async Task<T> RunQueryAtomAsync<T>(Query query, CancellationTo
if( typeof(T).IsJToken() )
{
var fmt = FormatOptions.FromOptArgs(query.GlobalOptions);
return (T)Converter.ConvertPseudoTypes(res.Data[0], fmt);
Converter.ConvertPseudoTypes(res.Data[0], fmt);
return (T)(object)res.Data[0]; //ugh ugly. find a better way to do this.
}
return res.Data[0].ToObject<T>(Converter.Serializer);

Expand Down Expand Up @@ -310,7 +311,8 @@ private async Task<T> RunQueryResultAsync<T>(Query query, CancellationToken canc
if( typeof(T).IsJToken() )
{
var fmt = FormatOptions.FromOptArgs(query.GlobalOptions);
return (T)Converter.ConvertPseudoTypes(res.Data[0], fmt);
Converter.ConvertPseudoTypes(res.Data[0], fmt);
return (T)(object)res.Data[0]; //ugh ugly. find a better way to do this.
}
return res.Data[0].ToObject<T>(Converter.Serializer);
}
Expand All @@ -324,7 +326,8 @@ private async Task<T> RunQueryResultAsync<T>(Query query, CancellationToken canc
if( typeof(T).IsJToken() )
{
var fmt = FormatOptions.FromOptArgs(query.GlobalOptions);
return (T)Converter.ConvertPseudoTypes(res.Data, fmt);
Converter.ConvertPseudoTypes(res.Data, fmt);
return (T)(object)res.Data; //ugh ugly. find a better way to do this.
}
return res.Data.ToObject<T>(Converter.Serializer);
}
Expand Down Expand Up @@ -381,7 +384,8 @@ protected async Task<dynamic> RunQueryAsync<T>(Query query, CancellationToken ca
if( typeof(T).IsJToken() )
{
var fmt = FormatOptions.FromOptArgs(query.GlobalOptions);
return Converter.ConvertPseudoTypes(res.Data[0], fmt);
Converter.ConvertPseudoTypes(res.Data[0], fmt);
return res.Data[0];
}
return res.Data[0].ToObject(typeof(T), Converter.Serializer);
}
Expand Down
4 changes: 1 addition & 3 deletions Source/RethinkDb.Driver/Net/Converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public static void InitializeDefault()
/// <summary>
/// Method for converting pseudo types in JToken (JObjects)
/// </summary>
public static object ConvertPseudoTypes(JToken data, FormatOptions fmt)
public static void ConvertPseudoTypes(JToken data, FormatOptions fmt)
{
var reqlTypes = data.SelectTokens("$..$reql_type$").ToList();

Expand Down Expand Up @@ -143,8 +143,6 @@ public static object ConvertPseudoTypes(JToken data, FormatOptions fmt)

pesudoObject.Replace(convertedValue);
}

return data;
}

private static object GetTime(JObject value)
Expand Down
3 changes: 2 additions & 1 deletion Source/RethinkDb.Driver/Net/Cursor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ T Convert(JToken token)
{
if( typeof(T).IsJToken() )
{
return (T)Converter.ConvertPseudoTypes(token, fmt);
Converter.ConvertPseudoTypes(token, fmt);
return (T)(object)token; //ugh ugly. find a better way to do this.
}
return token.ToObject<T>(Converter.Serializer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,14 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s
}

//http://stackoverflow.com/questions/2883576/how-do-you-convert-epoch-time-in-c
public double ToUnixTime(DateTimeOffset date)
public static double ToUnixTime(DateTimeOffset date)
{
return (date.UtcTicks - 621355968000000000) / 10000000.0;
return ToUnixTime(date.UtcTicks);
}

public static double ToUnixTime(long utcTicks)
{
return (utcTicks - 621355968000000000) / 10000000.0;
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
Expand Down
1 change: 1 addition & 0 deletions Source/RethinkDb.Driver/RethinkDb.Driver.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<ItemGroup>
<Compile Include="AssemblyAttributes.cs" />
<Compile Include="Ast\ExtensionHelper.cs" />
<Compile Include="Ast\TopLevel.cs" />
<Compile Include="Ast\Poco.cs" />
<Compile Include="Ast\PropertyHelper.cs" />
<Compile Include="Ast\Query.cs" />
Expand Down
2 changes: 1 addition & 1 deletion Source/Templates/CodeGen/TopLevelTemplate.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ using RethinkDb.Driver.Model;
using RethinkDb.Driver.Ast;

namespace RethinkDb.Driver.Ast {
public class TopLevel {
public partial class TopLevel {

public ReqlExpr Expr(Object value){
return Util.ToReqlExpr(value);
Expand Down
Loading

0 comments on commit 90734af

Please sign in to comment.