예제 #1
0
        /// <summary>
        /// Takes in records and writes them out to the Sage instance then sends acks back to the client
        /// </summary>
        /// <param name="requestStream"></param>
        /// <param name="responseStream"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task WriteStream(IAsyncStreamReader <Record> requestStream,
                                               IServerStreamWriter <RecordAck> responseStream, ServerCallContext context)
        {
            try
            {
                Logger.Info("Writing records to Sage...");
                var schema   = _server.WriteSettings.Schema;
                var sla      = _server.WriteSettings.CommitSLA;
                var inCount  = 0;
                var outCount = 0;

                // get next record to publish while connected and configured
                while (await requestStream.MoveNext(context.CancellationToken) && _server.Connected &&
                       _server.WriteConfigured)
                {
                    var record = requestStream.Current;
                    inCount++;

                    Logger.Debug($"Got Record: {record.DataJson}");

                    // send record to source system
                    // timeout if it takes longer than the sla
                    var task = Task.Run(() => PutRecord(schema, record));
                    if (task.Wait(TimeSpan.FromSeconds(sla)))
                    {
                        // send ack
                        var ack = new RecordAck
                        {
                            CorrelationId = record.CorrelationId,
                            Error         = task.Result
                        };
                        await responseStream.WriteAsync(ack);

                        if (String.IsNullOrEmpty(task.Result))
                        {
                            outCount++;
                        }
                    }
                    else
                    {
                        // send timeout ack
                        var ack = new RecordAck
                        {
                            CorrelationId = record.CorrelationId,
                            Error         = "timed out"
                        };
                        await responseStream.WriteAsync(ack);
                    }
                }

                Logger.Info($"Wrote {outCount} of {inCount} records to Sage.");
            }
            catch (Exception e)
            {
                Logger.Error(e, e.Message, context);
            }
        }
예제 #2
0
        public static async Task <string> WriteRecordAsync(IApiClient apiClient, Schema schema, Record record,
                                                           IServerStreamWriter <RecordAck> responseStream)
        {
            // debug
            Logger.Debug($"Starting timer for {record.RecordId}");
            var timer = Stopwatch.StartNew();

            try
            {
                var endpoint = EndpointHelper.GetEndpointForSchema(schema);

                if (endpoint == null)
                {
                    throw new Exception($"Endpoint {schema.Id} does not exist");
                }

                // debug
                Logger.Debug(JsonConvert.SerializeObject(record, Formatting.Indented));

                // semaphore
                await WriteSemaphoreSlim.WaitAsync();

                // write records
                var errorMessage = await endpoint.WriteRecordAsync(apiClient, schema, record, responseStream);

                if (!string.IsNullOrWhiteSpace(errorMessage))
                {
                    Logger.Error(new Exception(errorMessage), errorMessage);
                }

                timer.Stop();
                Logger.Debug($"Acknowledged Record {record.RecordId} time: {timer.ElapsedMilliseconds}");

                return("");
            }
            catch (Exception e)
            {
                Logger.Error(e, $"Error writing record {e.Message}");
                // send ack
                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = e.Message
                };
                await responseStream.WriteAsync(ack);

                timer.Stop();
                Logger.Debug($"Failed Record {record.RecordId} time: {timer.ElapsedMilliseconds}");

                return(e.Message);
            }
            finally
            {
                WriteSemaphoreSlim.Release();
            }
        }
예제 #3
0
        /// <summary>
        /// Adds and removes records to replication db
        /// Adds and updates available shapes
        /// </summary>
        /// <param name="connFactory"></param>
        /// <param name="schema"></param>
        /// <param name="record"></param>
        /// <param name="config"></param>
        /// <param name="responseStream"></param>
        /// <returns>Error message string</returns>
        public static async Task <string> WriteRecordAsync(IConnectionFactory connectionFactory, Schema schema, Record record, ConfigureReplicationFormData config, IServerStreamWriter <RecordAck> responseStream)
        {
            Logger.Debug($"Starting {record.RecordId}");
            Stopwatch timer = Stopwatch.StartNew();

            try
            {
                Logger.Debug(JsonConvert.SerializeObject(record, Formatting.Indented));

                await WriteSemaphoreSlim.WaitAsync();

                string safeSchemaName       = config.SchemaName;
                string safeGoldenTableName  = config.GoldenTableName;
                string safeVersionTableName = config.VersionTableName;

                ReplicationTable goldenTable  = GetGoldenReplicationTable(schema, safeSchemaName, safeGoldenTableName);
                ReplicationTable versionTable = GetVersionReplicationTable(schema, safeSchemaName, safeVersionTableName);

                List <string> recordVersionIds = record.Versions.Select(r => r.RecordId).ToList();
                // GetNamed Record Data
                // TODO: Finish

                return(null);
            }
            catch (Exception ex)
            {
                Logger.Error(ex, $"Error writing record: {ex.Message}");

                RecordAck ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = ex.Message
                };

                await responseStream.WriteAsync(ack);

                timer.Stop();

                return(ex.Message);
            }
            finally
            {
                Logger.Debug($"Stopped {record.RecordId}. Time: {timer.ElapsedMilliseconds}");
                WriteSemaphoreSlim.Release();
            }
        }
예제 #4
0
        /// <summary>
        /// Adds and removes records to replication db
        /// Adds and updates available shapes
        /// </summary>
        /// <param name="connFactory"></param>
        /// <param name="schema"></param>
        /// <param name="record"></param>
        /// <param name="config"></param>
        /// <param name="responseStream"></param>
        /// <returns>Error message string</returns>
        public static async Task <string> WriteRecord(IConnectionFactory connFactory, Schema schema, Record record,
                                                      ConfigureReplicationFormData config, IServerStreamWriter <RecordAck> responseStream)
        {
            // debug
            Logger.Debug($"Starting timer for {record.RecordId}");
            var timer = Stopwatch.StartNew();

            try
            {
                // debug
                Logger.Debug(JsonConvert.SerializeObject(record, Formatting.Indented));

                // semaphore
                await ReplicationSemaphoreSlim.WaitAsync();

                // setup
                var safeSchemaName       = config.SchemaName;
                var safeGoldenTableName  = config.GoldenTableName;
                var safeVersionTableName = config.VersionTableName;

                var goldenTable  = GetGoldenReplicationTable(schema, safeSchemaName, safeGoldenTableName);
                var versionTable = GetVersionReplicationTable(schema, safeSchemaName, safeVersionTableName);

                // transform data
                var recordVersionIds = record.Versions.Select(v => v.RecordId).ToList();
                var recordData       = GetNamedRecordData(schema, record.DataJson);
                recordData[Constants.ReplicationRecordId]   = record.RecordId;
                recordData[Constants.ReplicationVersionIds] = recordVersionIds;

                // get previous golden record
                List <string> previousRecordVersionIds;
                if (await RecordExistsAsync(connFactory, goldenTable, record.RecordId))
                {
                    var recordMap = await GetRecordAsync(connFactory, goldenTable, record.RecordId);

                    if (recordMap.ContainsKey(Constants.ReplicationVersionIds))
                    {
                        previousRecordVersionIds =
                            JsonConvert.DeserializeObject <List <string> >(recordMap[Constants.ReplicationVersionIds].ToString());
                    }
                    else
                    {
                        previousRecordVersionIds = recordVersionIds;
                    }
                }
                else
                {
                    previousRecordVersionIds = recordVersionIds;
                }

                // write data
                // check if 2 since we always add 2 things to the dictionary
                if (recordData.Count == 2)
                {
                    // delete everything for this record
                    Logger.Debug($"shapeId: {safeSchemaName} | recordId: {record.RecordId} - DELETE");
                    await DeleteRecordAsync(connFactory, goldenTable, record.RecordId);

                    foreach (var versionId in previousRecordVersionIds)
                    {
                        Logger.Debug(
                            $"shapeId: {safeSchemaName} | recordId: {record.RecordId} | versionId: {versionId} - DELETE");
                        await DeleteRecordAsync(connFactory, versionTable, versionId);
                    }
                }
                else
                {
                    // update record and remove/add versions
                    Logger.Debug($"shapeId: {safeSchemaName} | recordId: {record.RecordId} - UPSERT");
                    await UpsertRecordAsync(connFactory, goldenTable, recordData);

                    // delete missing versions
                    var missingVersions = previousRecordVersionIds.Except(recordVersionIds);
                    foreach (var versionId in missingVersions)
                    {
                        Logger.Debug(
                            $"shapeId: {safeSchemaName} | recordId: {record.RecordId} | versionId: {versionId} - DELETE");
                        await DeleteRecordAsync(connFactory, versionTable, versionId);
                    }

                    // upsert other versions
                    foreach (var version in record.Versions)
                    {
                        Logger.Debug(
                            $"shapeId: {safeSchemaName} | recordId: {record.RecordId} | versionId: {version.RecordId} - UPSERT");
                        var versionData = GetNamedRecordData(schema, version.DataJson);
                        versionData[Constants.ReplicationVersionRecordId] = version.RecordId;
                        versionData[Constants.ReplicationRecordId]        = record.RecordId;
                        await UpsertRecordAsync(connFactory, versionTable, versionData);
                    }
                }

                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = ""
                };
                await responseStream.WriteAsync(ack);

                timer.Stop();
                Logger.Debug($"Acknowledged Record {record.RecordId} time: {timer.ElapsedMilliseconds}");

                return("");
            }
            catch (Exception e)
            {
                Logger.Error(e, $"Error replicating records {e.Message}");
                // send ack
                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = e.Message
                };
                await responseStream.WriteAsync(ack);

                timer.Stop();
                Logger.Debug($"Failed Record {record.RecordId} time: {timer.ElapsedMilliseconds}");

                return(e.Message);
            }
            finally
            {
                ReplicationSemaphoreSlim.Release();
            }
        }
예제 #5
0
        /// <summary>
        /// Writes records to Sisense
        /// </summary>
        /// <param name="requestStream"></param>
        /// <param name="responseStream"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public override async Task WriteStream(IAsyncStreamReader <Record> requestStream,
                                               IServerStreamWriter <RecordAck> responseStream, ServerCallContext context)
        {
            try
            {
                Logger.Info("Writing records to Sisense...");
                Logger.Info($"API Route: {GetBindingHostedService.ServerAddresses.Addresses.FirstOrDefault()}");

                var schema   = _server.WriteSettings.Schema;
                var sla      = _server.WriteSettings.CommitSLA;
                var inCount  = 0;
                var outCount = 0;

                // get next record to publish while connected and configured
                while (await requestStream.MoveNext(context.CancellationToken) && _server.Connected &&
                       _server.WriteConfigured)
                {
                    var record = requestStream.Current;
                    inCount++;

                    Logger.Debug($"Got record: {record.DataJson}");

                    if (_server.WriteSettings.IsReplication())
                    {
                        var config = JsonConvert.DeserializeObject <ConfigureReplicationFormData>(_server.WriteSettings.Replication.SettingsJson);

                        // send record to source system
                        // timeout if it takes longer than the sla
                        var task = Task.Run(() => Replication.WriteRecord(schema, record, config));
                        if (task.Wait(TimeSpan.FromSeconds(sla)))
                        {
                            // send ack
                            var ack = new RecordAck
                            {
                                CorrelationId = record.CorrelationId,
                                Error         = task.Result
                            };
                            await responseStream.WriteAsync(ack);

                            if (String.IsNullOrEmpty(task.Result))
                            {
                                outCount++;
                            }
                        }
                        else
                        {
                            // send timeout ack
                            var ack = new RecordAck
                            {
                                CorrelationId = record.CorrelationId,
                                Error         = "timed out"
                            };
                            await responseStream.WriteAsync(ack);
                        }
                    }
                    else
                    {
                        throw new Exception("Only replication writebacks are supported");
                    }
                }

                Logger.Info($"Wrote {outCount} of {inCount} records to Sisense.");
            }
            catch (Exception e)
            {
                Logger.Error(e.Message);
                throw;
            }
        }
예제 #6
0
        public static async Task <string> WriteRecordAsync(IConnectionFactory connFactory, Schema schema, Record record,
                                                           IServerStreamWriter <RecordAck> responseStream)
        {
            // debug
            Logger.Debug($"Starting timer for {record.RecordId}");
            var timer = Stopwatch.StartNew();

            var conn = connFactory.GetConnection();

            try
            {
                var recordMap = JsonConvert.DeserializeObject <Dictionary <string, object> >(record.DataJson);

                // debug
                Logger.Debug(JsonConvert.SerializeObject(record, Formatting.Indented));

                // semaphore
                await WriteSemaphoreSlim.WaitAsync();

                // call stored procedure
                var querySb = new StringBuilder($"CALL {schema.Query}(");

                foreach (var property in schema.Properties)
                {
                    if (!recordMap.ContainsKey(property.Id))
                    {
                        throw new Exception($"{property.Id} is required by the stored procedure and is not mapped on the job.");
                    }

                    var rawValue = recordMap[property.Id];

                    if (rawValue == null || string.IsNullOrWhiteSpace(rawValue.ToString()))
                    {
                        querySb.Append("NULL,");
                    }
                    else
                    {
                        querySb.Append($"'{Utility.Utility.GetSafeString(Utility.Utility.GetSafeString(rawValue.ToString(), "'", "''"))}',");
                    }
                }

                querySb.Length--;
                querySb.Append(")");

                var query = querySb.ToString();

                Logger.Debug($"WB querySb: {query}");

                await conn.OpenAsync();

                var cmd = connFactory.GetCommand(query, conn);

                await cmd.ExecuteNonQueryAsync();

                await conn.CloseAsync();

                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = ""
                };
                await responseStream.WriteAsync(ack);

                timer.Stop();
                Logger.Debug($"Acknowledged Record {record.RecordId} time: {timer.ElapsedMilliseconds}");

                return("");
            }
            catch (Exception e)
            {
                await conn.CloseAsync();

                Logger.Error(e, $"Error writing record {e.Message}");
                // send ack
                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = e.Message
                };
                await responseStream.WriteAsync(ack);

                timer.Stop();
                Logger.Debug($"Failed Record {record.RecordId} time: {timer.ElapsedMilliseconds}");

                return(e.Message);
            }
            finally
            {
                WriteSemaphoreSlim.Release();
            }
        }
예제 #7
0
        /// <summary>
        /// Adds and removes records to local replication db
        /// Adds and updates available shapes
        /// </summary>
        /// <param name="clusterFactory"></param>
        /// <param name="schema"></param>
        /// <param name="record"></param>
        /// <param name="config"></param>
        /// <param name="responseStream"></param>
        /// <returns>Error message string</returns>
        public static async Task <string> WriteRecord(IClusterFactory clusterFactory, Schema schema, Record record,
                                                      ConfigureReplicationFormData config, IServerStreamWriter <RecordAck> responseStream)
        {
            // debug
            Logger.Debug($"Starting timer for {record.RecordId}");
            var timer = Stopwatch.StartNew();

            try
            {
                // debug
                Logger.Debug(JsonConvert.SerializeObject(record, Formatting.Indented));

                // semaphore
                await ReplicationSemaphoreSlim.WaitAsync();

                // setup
                var safeShapeName        = schema.Name;
                var safeGoldenBucketName =
                    string.Concat(config.GoldenBucketName.Where(c => !char.IsWhiteSpace(c)));
                var safeVersionBucketName =
                    string.Concat(config.VersionBucketName.Where(c => !char.IsWhiteSpace(c)));

                var goldenBucket = await clusterFactory.GetBucketAsync(safeGoldenBucketName);

                var versionBucket = await clusterFactory.GetBucketAsync(safeVersionBucketName);

                // transform data
                var recordVersionIds = record.Versions.Select(v => v.RecordId).ToList();
                var recordData       = GetNamedRecordData(schema, record.DataJson);
                recordData[Constants.NaveegoVersionIds] = recordVersionIds;

                // get previous golden record
                List <string> previousRecordVersionIds;
                if (await goldenBucket.ExistsAsync(record.RecordId))
                {
                    var result = await goldenBucket.GetAsync <Dictionary <string, object> >(record.RecordId);

                    if (result.Value.ContainsKey(Constants.NaveegoVersionIds))
                    {
                        previousRecordVersionIds =
                            JsonConvert.DeserializeObject <List <string> >(
                                JsonConvert.SerializeObject(result.Value[Constants.NaveegoVersionIds]));
                    }
                    else
                    {
                        previousRecordVersionIds = recordVersionIds;
                    }
                }
                else
                {
                    previousRecordVersionIds = recordVersionIds;
                }

                // write data
                if (recordData.Count == 0)
                {
                    // delete everything for this record
                    Logger.Debug($"shapeId: {safeShapeName} | recordId: {record.RecordId} - DELETE");
                    var result = await goldenBucket.RemoveAsync(record.RecordId);

                    result.EnsureSuccess();

                    foreach (var versionId in previousRecordVersionIds)
                    {
                        Logger.Debug(
                            $"shapeId: {safeShapeName} | recordId: {record.RecordId} | versionId: {versionId} - DELETE");
                        result = await versionBucket.RemoveAsync(versionId);

                        result.EnsureSuccess();
                    }
                }
                else
                {
                    // update record and remove/add versions
                    Logger.Debug($"shapeId: {safeShapeName} | recordId: {record.RecordId} - UPSERT");
                    var result = await goldenBucket.UpsertAsync(record.RecordId, recordData);

                    result.EnsureSuccess();

                    // delete missing versions
                    var missingVersions = previousRecordVersionIds.Except(recordVersionIds);
                    foreach (var versionId in missingVersions)
                    {
                        Logger.Debug(
                            $"shapeId: {safeShapeName} | recordId: {record.RecordId} | versionId: {versionId} - DELETE");
                        var versionDeleteResult = await versionBucket.RemoveAsync(versionId);

                        versionDeleteResult.EnsureSuccess();
                    }

                    // upsert other versions
                    foreach (var version in record.Versions)
                    {
                        Logger.Debug(
                            $"shapeId: {safeShapeName} | recordId: {record.RecordId} | versionId: {version.RecordId} - UPSERT");
                        var versionData         = GetNamedRecordData(schema, version.DataJson);
                        var versionUpsertResult = await versionBucket.UpsertAsync(version.RecordId, versionData);

                        versionUpsertResult.EnsureSuccess();
                    }
                }

                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = ""
                };
                await responseStream.WriteAsync(ack);

                timer.Stop();
                Logger.Debug($"Acknowledged Record {record.RecordId} time: {timer.ElapsedMilliseconds}");

                return("");
            }
            catch (Exception e)
            {
                Logger.Error(e, $"Error replicating records {e.Message}");
                // send ack
                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = e.Message
                };
                await responseStream.WriteAsync(ack);

                timer.Stop();
                Logger.Debug($"Failed Record {record.RecordId} time: {timer.ElapsedMilliseconds}");

                return(e.Message);
            }
            finally
            {
                ReplicationSemaphoreSlim.Release();
            }
        }
예제 #8
0
        public virtual async Task <string> WriteRecordAsync(IApiClient apiClient, Schema schema, Record record,
                                                            IServerStreamWriter <RecordAck> responseStream)
        {
            var recordMap = JsonConvert.DeserializeObject <Dictionary <string, object> >(record.DataJson);

            foreach (var requiredPropertyId in RequiredWritePropertyIds)
            {
                if (!recordMap.ContainsKey(requiredPropertyId))
                {
                    var errorMessage = $"Record did not contain required property {requiredPropertyId}";
                    var errorAck     = new RecordAck
                    {
                        CorrelationId = record.CorrelationId,
                        Error         = errorMessage
                    };
                    await responseStream.WriteAsync(errorAck);

                    return(errorMessage);
                }

                if (recordMap.ContainsKey(requiredPropertyId) && recordMap[requiredPropertyId] == null)
                {
                    var errorMessage = $"Required property {requiredPropertyId} was NULL";
                    var errorAck     = new RecordAck
                    {
                        CorrelationId = record.CorrelationId,
                        Error         = errorMessage
                    };
                    await responseStream.WriteAsync(errorAck);

                    return(errorMessage);
                }
            }

            var postObject = new Dictionary <string, object>();

            foreach (var property in schema.Properties)
            {
                object value = null;

                if (recordMap.ContainsKey(property.Id))
                {
                    value = recordMap[property.Id];
                }

                postObject.TryAdd(property.Id, value);
            }

            var json = new StringContent(
                JsonConvert.SerializeObject(postObject),
                Encoding.UTF8,
                "application/json"
                );

            HttpResponseMessage response;

            if (!recordMap.ContainsKey(WritePathPropertyId) || recordMap.ContainsKey(WritePathPropertyId) &&
                recordMap[WritePathPropertyId] == null)
            {
                response =
                    await apiClient.PostAsync($"{BasePath.TrimEnd('/')}", json);
            }
            else
            {
                response =
                    await apiClient.PutAsync($"{BasePath.TrimEnd('/')}/{recordMap[WritePathPropertyId]}", json);
            }

            if (!response.IsSuccessStatusCode)
            {
                var errorMessage = await response.Content.ReadAsStringAsync();

                var errorAck = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = errorMessage
                };
                await responseStream.WriteAsync(errorAck);

                return(errorMessage);
            }

            var ack = new RecordAck
            {
                CorrelationId = record.CorrelationId,
                Error         = ""
            };
            await responseStream.WriteAsync(ack);

            return("");
        }
예제 #9
0
        /// <summary>
        /// Adds and removes records to replication db
        /// Adds and updates available shapes
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="schema"></param>
        /// <param name="record"></param>
        /// <param name="config"></param>
        /// <param name="responseStream"></param>
        /// <returns>Error message string</returns>
        public static async Task <string> WriteRecordAsync(SqlDatabaseConnection conn, Schema schema, Record record,
                                                           ConfigureReplicationFormData config, IServerStreamWriter <RecordAck> responseStream)
        {
            // debug
            Logger.Debug($"Starting timer for {record.RecordId}");
            var timer = Stopwatch.StartNew();

            try
            {
                // debug
                Logger.Debug(JsonConvert.SerializeObject(record, Formatting.Indented));

                // semaphore
                await WriteSemaphoreSlim.WaitAsync();

                // setup
                var safeSchemaName      = Constants.SchemaName;
                var safeTargetTableName = config.GetGoldenTableName();

                var targetTable =
                    Replication.Replication.GetGoldenReplicationTable(schema, safeSchemaName, safeTargetTableName);

                // get record
                var recordData = JsonConvert.DeserializeObject <Dictionary <string, object> >(record.DataJson);
                recordData[Constants.ReplicationRecordId]   = record.RecordId;
                recordData[Constants.ReplicationVersionIds] = null;

                // write data
                if (recordData.Count == 2)
                {
                    // delete record
                    Logger.Debug($"shapeId: {safeSchemaName} | recordId: {record.RecordId} - DELETE");
                    await Replication.Replication.DeleteRecordAsync(conn, targetTable, record.RecordId);
                }
                else
                {
                    // add in all default values
                    foreach (var property in schema.Properties)
                    {
                        if (!recordData.ContainsKey(property.Id) && !string.IsNullOrWhiteSpace(property.PublisherMetaJson))
                        {
                            Logger.Debug("adding default value");
                            var columnConfig = JsonConvert.DeserializeObject <WriteColumn>(property.PublisherMetaJson);
                            recordData[property.Id] = columnConfig.DefaultValue;
                        }
                    }

                    Logger.Debug(JsonConvert.SerializeObject(recordData, Formatting.Indented));

                    // update record
                    Logger.Debug($"shapeId: {safeSchemaName} | recordId: {record.RecordId} - UPSERT");
                    await Replication.Replication.UpsertRecordAsync(conn, targetTable, recordData);
                }

                // set triggers for async file write
                LastWriteTime = DateTime.Now;
                PendingWrites = true;

                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = ""
                };
                await responseStream.WriteAsync(ack);

                timer.Stop();
                Logger.Debug($"Acknowledged Record {record.RecordId} time: {timer.ElapsedMilliseconds}");

                return("");
            }
            catch (Exception e)
            {
                Logger.Error(e, $"Error replicating records {e.Message}");
                // send ack
                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = e.Message
                };
                await responseStream.WriteAsync(ack);

                timer.Stop();
                Logger.Debug($"Failed Record {record.RecordId} time: {timer.ElapsedMilliseconds}");

                if (e.Message.Contains("library routine called out of sequence"))
                {
                    throw;
                }

                return(e.Message);
            }
            finally
            {
                WriteSemaphoreSlim.Release();
            }
        }
예제 #10
0
            public override async Task <string> WriteRecordAsync(IApiClient apiClient, Schema schema, Record record,
                                                                 IServerStreamWriter <RecordAck> responseStream)
            {
                var recordMap = JsonConvert.DeserializeObject <Dictionary <string, object> >(record.DataJson);

                foreach (var requiredPropertyId in RequiredWritePropertyIds)
                {
                    if (!recordMap.ContainsKey(requiredPropertyId))
                    {
                        var errorMessage = $"Record did not contain required property {requiredPropertyId}";
                        var errorAck     = new RecordAck
                        {
                            CorrelationId = record.CorrelationId,
                            Error         = errorMessage
                        };
                        await responseStream.WriteAsync(errorAck);

                        return(errorMessage);
                    }

                    if (recordMap.ContainsKey(requiredPropertyId) && recordMap[requiredPropertyId] == null)
                    {
                        var errorMessage = $"Required property {requiredPropertyId} was NULL";
                        var errorAck     = new RecordAck
                        {
                            CorrelationId = record.CorrelationId,
                            Error         = errorMessage
                        };
                        await responseStream.WriteAsync(errorAck);

                        return(errorMessage);
                    }
                }

                // get live object
                Dictionary <string, object> liveRecord = new Dictionary <string, object>();
                var getResponse = await apiClient.GetAsync(
                    $"{BasePath.TrimEnd('/')}/{recordMap[DetailPropertyId]}/{DetailPath.TrimStart('/')}");

                if (getResponse.IsSuccessStatusCode)
                {
                    liveRecord =
                        JsonConvert.DeserializeObject <Dictionary <string, object> >(await getResponse.Content.ReadAsStringAsync());
                }

                var putObject         = new Dictionary <string, object>();
                var customFieldObject = new List <CustomField>();

                foreach (var property in schema.Properties)
                {
                    if (property.TypeAtSource == Constants.CustomProperty)
                    {
                        var customField = new CustomField
                        {
                            FieldName = property.Id,
                            Value     = null
                        };

                        if (recordMap.ContainsKey(property.Id))
                        {
                            customField.Value = recordMap[property.Id];
                        }

                        customFieldObject.Add(customField);
                    }
                    else
                    {
                        object value = null;

                        if (recordMap.ContainsKey(property.Id))
                        {
                            value = recordMap[property.Id];

                            if (property.Type == PropertyType.Json)
                            {
                                if (value is string s)
                                {
                                    value = JsonConvert.DeserializeObject(s) ?? null;
                                }
                            }
                        }

                        putObject.Add(property.Id, value);
                    }
                }

                putObject.Add("CustomFields", customFieldObject);

                if (putObject.ContainsKey("Lists") && putObject["Lists"] != null)
                {
                    JArray j = (JArray)putObject["Lists"];
                    putObject["Lists"] = j.Select(x => x["ListID"]).ToList();
                }
                else
                {
                    JArray j = (JArray)liveRecord["Lists"];
                    putObject["Lists"] = j.Select(x => x["ListID"]).ToList();
                }

                if (putObject.ContainsKey("Publications") && putObject["Publications"] != null)
                {
                    JArray j = (JArray)putObject["Publications"];
                    putObject["Publications"] = j.Select(x => x["PublicationID"]).ToList();
                }
                else
                {
                    JArray j = (JArray)liveRecord["Publications"];
                    putObject["Publications"] = j.Select(x => x["PublicationID"]).ToList();
                }

                if (putObject.ContainsKey("Source") && putObject["Source"] != null)
                {
                    JObject j = (JObject)putObject["Source"];
                    if (j.ContainsKey("SourceID"))
                    {
                        putObject["SourceID"] = j["SourceID"];
                    }
                    putObject.Remove("Source");
                }
                else
                {
                    JObject j = (JObject)liveRecord["Source"];
                    if (j.ContainsKey("SourceID"))
                    {
                        putObject["SourceID"] = j["SourceID"];
                    }
                    putObject.Remove("Source");
                }

                var json = new StringContent(
                    JsonConvert.SerializeObject(putObject),
                    Encoding.UTF8,
                    "application/json"
                    );

                var response =
                    await apiClient.PutAsync($"{BasePath.TrimEnd('/')}/{recordMap[WritePathPropertyId]}", json);

                if (response.StatusCode == HttpStatusCode.NotFound)
                {
                    response =
                        await apiClient.PostAsync($"{BasePath.TrimEnd('/')}", json);
                }

                if (!response.IsSuccessStatusCode)
                {
                    var errorMessage = await response.Content.ReadAsStringAsync();

                    var errorAck = new RecordAck
                    {
                        CorrelationId = record.CorrelationId,
                        Error         = errorMessage
                    };
                    await responseStream.WriteAsync(errorAck);

                    return(errorMessage);
                }

                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = ""
                };
                await responseStream.WriteAsync(ack);

                return("");
            }
예제 #11
0
            public override async Task <string> WriteRecordAsync(IApiClient apiClient, Schema schema, Record record,
                                                                 IServerStreamWriter <RecordAck> responseStream)
            {
                var recordMap = JsonConvert.DeserializeObject <Dictionary <string, object> >(record.DataJson);

                foreach (var requiredPropertyId in RequiredWritePropertyIds)
                {
                    if (!recordMap.ContainsKey(requiredPropertyId))
                    {
                        var errorMessage = $"Record did not contain required property {requiredPropertyId}";
                        var errorAck     = new RecordAck
                        {
                            CorrelationId = record.CorrelationId,
                            Error         = errorMessage
                        };
                        await responseStream.WriteAsync(errorAck);

                        return(errorMessage);
                    }

                    if (recordMap.ContainsKey(requiredPropertyId) && recordMap[requiredPropertyId] == null)
                    {
                        var errorMessage = $"Required property {requiredPropertyId} was NULL";
                        var errorAck     = new RecordAck
                        {
                            CorrelationId = record.CorrelationId,
                            Error         = errorMessage
                        };
                        await responseStream.WriteAsync(errorAck);

                        return(errorMessage);
                    }
                }

                // only preload conversion dictionary if empty
                if (NameToListIdDictionary.IsEmpty)
                {
                    await PreLoadLookup(apiClient);
                }

                // attempt to add email
                var errorString = await AddEmailToList(apiClient, schema, recordMap);

                if (!string.IsNullOrWhiteSpace(errorString))
                {
                    errorString = await AddNewList(apiClient, schema, recordMap);

                    // add email to new list if created
                    if (string.IsNullOrWhiteSpace(errorString))
                    {
                        errorString = await AddEmailToList(apiClient, schema, recordMap);
                    }
                }

                if (!string.IsNullOrWhiteSpace(errorString))
                {
                    var errorMessage = errorString;
                    var errorAck     = new RecordAck
                    {
                        CorrelationId = record.CorrelationId,
                        Error         = errorMessage
                    };
                    await responseStream.WriteAsync(errorAck);

                    return(errorMessage);
                }

                var ack = new RecordAck
                {
                    CorrelationId = record.CorrelationId,
                    Error         = ""
                };
                await responseStream.WriteAsync(ack);

                return("");
            }