moq mongodb InsertOneAsync method - mongodb

I am using Mongodb database with .net core. I just want to moq insert method that using mongodbContext. Here is what I am trying to do but it's not working:
public void InsertEventAsync_Test()
{
//Arrange
var eventRepository = EventRepository();
var pEvent = new PlanEvent
{
ID = "testEvent",
WorkOrderID = "WorkOrderID",
IsDeleted = false,
IsActive = true,
EquipmentID = "EquipmentID"
};
////Act
//mockEventContext.Setup(mr => mr.PlanEvent.InsertOne(It.IsAny<PlanEvent>(), It.IsAny<InsertOneOptions>()))
mockEventContext.Setup(s => s.PlanEvent.InsertOneAsync(It.IsAny<PlanEvent>(), It.IsAny<InsertOneOptions>())).Returns("sdad");
var result = eventRepository.InsertEventAsync(pEvent);
////Assert
result.Should().NotBeNull();
}
Below is the method that I need to Moq:
public EventRepository(IFMPContext eventContext)
{
_eventContext = eventContext;
}
public async Task<string> InsertEventAsync(Model.EventDataModel.PlanEvent eventobj)
{
eventobj._id = ObjectId.GenerateNewId();
eventobj.CreatedDateTime = DateTime.UtcNow.ToString();
try
{
_eventContext.PlanEvent.InsertOne(eventobj);
return eventobj.ID;
}
catch (Exception ex)
{
string x = ex.Message;
}
return "";
}

Assuming
public class EventRepository {
private readonly IFMPContext eventContext;
public EventRepository(IFMPContext eventContext) {
this.eventContext = eventContext;
}
public async Task<string> InsertEventAsync(Model.EventDataModel.PlanEvent eventobj) {
eventobj._id = ObjectId.GenerateNewId();
eventobj.CreatedDateTime = DateTime.UtcNow.ToString();
try {
await eventContext.PlanEvent.InsertOneAsync(eventobj);
return eventobj.ID;
} catch (Exception ex) {
string x = ex.Message;
}
return "";
}
}
You need to configure the test to support the async nature of the method under test
public async Task InsertEventAsync_Test()
{
//Arrange
var expected = "testEvent";
var pEvent = new PlanEvent {
ID = expected,
WorkOrderID = "WorkOrderID",
IsDeleted = false,
IsActive = true,
EquipmentID = "EquipmentID"
};
var mockEventContext = new Mock<IFMPContext>();
mockEventContext
.Setup(_ => _.PlanEvent.InsertOneAsync(It.IsAny<PlanEvent>(), It.IsAny<InsertOneOptions>()))
.ReturnsAsync(Task.FromResult((object)null));
var eventRepository = new EventRepository(mockEventContext.Object);
//Act
var actual = await eventRepository.InsertEventAsync(pEvent);
//Assert
actual.Should().NotBeNull()
actual.Should().Be(expected);
}
The test method definition needed to be updated to be asynchronous to allow the method under test to be awaited. The mock dependency also needed to be setup in such a way to allow the async flow to continue as expected when invoked.

#Nkosi Thanks a lot for your help. Finally i found the way. i was missing extra moq param It.IsAny<System.Threading.CancellationToken>() below is the working test
public void InsertEventAsync_Test()
{
//Arrange
var eventRepository = EventRepository();
var pEvent = new PlanEvent
{
ID = "testEvent",
WorkOrderID = "WorkOrderID",
IsDeleted = false,
IsActive = true,
EquipmentID = "EquipmentID"
};
////Act
mockEventContext.Setup(s => s.PlanEvent.InsertOne(It.IsAny<PlanEvent>(), It.IsAny<InsertOneOptions>(),It.IsAny<System.Threading.CancellationToken>()));
var result = eventRepository.InsertEventAsync(pEvent);
////Assert
result.Should().NotBeNull();
Assert.AreEqual(pEvent.ID, result);
}

Related

OPC-DA AddGroup not working by using "TitaniumAS.Opc.Client" c# library

I'm getting a NULL reference error while adding the group. ie: in line -> opcDaGroup = server.AddGroup("Group");
Please help me out what's the issue and how to overcome the same.
whether this is the DCOM access issue.
Code:
public OpcDaGroup opcDaGroup;
private void StartConnection(string Cls_Id, string Machine_IpAddress)
{
try
{
log.Debug("START - Starting the connection");
Uri url = UrlBuilder.Build(Cls_Id, Machine_IpAddress);
server = new OpcDaServer(url);
server.Connect();
log.Debug("END - Starting the connection");
}
catch (Exception e)
{
log.Error("Could not connect to the OPC server for the IpAddress: {0}", e);
}
}
public OpcDaAdapterThreadImpl(string Cls_Id, string Machine_IpAddress, string name, ICollection<TagConfigurationModel> tags)
{
this.Cls_Id = Cls_Id;
this.Machine_IpAddress = Machine_IpAddress;
this.name = name;
this.AdapterReady = true;
Tags = tags;
var group = tags.GroupBy(c => new { ItemName = c.TagName }).Select(c => c.Key).Distinct();
grouptags = group.Select(c => new TagConfigurationModel { TagName = c.ItemName }).ToList();
opcTagItems = grouptags.Select(c => new OpcDaItemDefinition { ItemId = c.TagName, IsActive = true }).ToList();
// Connection Initializing
StartConnection(Cls_Id, Machine_IpAddress);
if (server.IsConnected)
{
// Add group to the server
opcDaGroup = server.AddGroup("Group");
opcDaGroup.IsActive = true;
// Add Items to group
opcDaGroup.AddItems(opcTagItems);
}
}

How to get user's password?

How to get user password with Vala? Secret.prompt.perform_sync() seems doesn't work.
I really don't know how use secret.prompt.
Main.vala:
class SecretServ : Secret.Service {
}
class SecPrompt : Secret.Prompt {
}
class ApplicationWindow : Gtk.ApplicationWindow {
construct {
show_all();
var ret = new VariantType("s");
string winid;
winid = "%d".printf((int) this.get_id());
SecPrompt prompt = new SecPrompt();
try {
prompt.perform_sync(winid, null, ret);
} catch (GLib.Error e) {
stderr.printf("ERROR: %s", e.message);
}
}
}
class Application : Gtk.Application {
public Application() {
Object(
application_id: "com.github.urandom0.pass",
flags: ApplicationFlags.FLAGS_NONE
);
}
protected override void activate() {
ApplicationWindow window = new ApplicationWindow();
this.add_window(window);
}
}
static int main(string[] args) {
var app = new Application();
return app.run(args);
}
Compile:
valac Main.vala --pkg=gtk+-3.0 --pkg=libsecret-1
Run:
./Main
You can't just create a Secret.Prompt like that. It is an object that is created by the libsecret library internally.
There is a Vala example on how to use libsecret here:
https://developer-old.gnome.org/libsecret/0.18/vala-examples.html
I will summarize the example as an answer below.
You have to first create a schema:
var example = new Secret.Schema ("org.example.Password", Secret.SchemaFlags.NONE,
"number", Secret.SchemaAttributeType.INTEGER,
"string", Secret.SchemaAttributeType.STRING,
"even", Secret.SchemaAttributeType.BOOLEAN);
Then you can store properties into the schema:
var attributes = new GLib.HashTable<string,string> ();
attributes["number"] = "8";
attributes["string"] = "eight";
attributes["even"] = "true";
Secret.password_storev.begin (example_schema, attributes, Secret.COLLECTION_DEFAULT,
"The label", "the password", null, (obj, async_res) => {
bool res = Secret.password_store.end (async_res);
/* ... do something now that the password has been stored */
});
And finally retrieve them again:
var attributes = new GLib.HashTable<string,string> ();
attributes["number"] = "8";
attributes["string"] = "eight";
attributes["even"] = "true";
Secret.password_lookupv.begin (example_schema, attributes, null, (obj, async_res) => {
string password = Secret.password_lookup.end (async_res);
});
For more details read the whole libsecret documentation at:
https://developer-old.gnome.org/libsecret/0.18/index.html

How do you create Expression<Func<T, T>> given the property names of T to map?

Here's what the required function would look like:
public static Expression<Func<T, T>> GetExpression<T>(string propertyNames) where T : class
{
var properties = propertyNames.Split(
new char[] { ',' },
StringSplitOptions.RemoveEmptyEntries
).ToList();
//need help here
}
Currently I'm doing it like this:
_context.Questions.Select(q =>
new Question() {
QuestionId = q.QuestionId,
QuestionEnglish = q.QuestionEnglish
}
).ToList();
And I want to replace it with:
_context.Questions.Select(GetExpression<Question>("QuestionId, QuestionInEnglish")).ToList();
Any help would be greatly appreciated.
You can do it like this;
public static Func<T, T> GetExpression<T>(string propertyNames)
{
var xParameter = Expression.Parameter(typeof(T), "parameter");
var xNew = Expression.New(typeof(T));
var selectFields = propertyNames.Split(',').Select(parameter => parameter.Trim())
.Select(parameter => {
var prop = typeof(T).GetProperty(parameter);
if (prop == null) // The field doesn't exist
{
return null;
}
var xOriginal = Expression.Property(xParameter, prop);
return Expression.Bind(prop, xOriginal);
}
).Where(x => x != null);
var lambda = Expression.Lambda<Func<T, T>>(Expression.MemberInit(xNew, selectFields), xParameter);
return lambda.Compile();
}
Usage;
var list = new List<Question>{new Question{QuestionEnglish = "QuestionName",QuestionId = 1}};
var result = list.Select(GetExpression<Question>("QuestionId, QuestionEnglish"));

opcua session was closed by client

I have written the attached OpcUaConnector class for opc-ua connection related activities.
But it is not handling session. For example:
In opc ua configuration disabled the endpoint
In kepserver configuration did runtime > reinitializing
The windows service is throwing:
Source : system.Reactive.Core
InnerException : The session was closed by client
and stopping the windows service, as this error goes unhandled.
Can some one suggest how to handle session in opc-ua?
public class OpcUaConnector
{
private static SimplerAES simplerAES = new SimplerAES();
private DataContainer dataCointainer = null;
private UaTcpSessionChannel channel;
private string opcServerName = string.Empty;
private string opcUserId = string.Empty;
private string opcPassword = string.Empty;
private static ILog LogOpcStore;
private static System.IDisposable token;
private static uint id;
public OpcConnector(ILog Log)
{
IntializeLogOpcStore(Log);
}
private static void IntializeLogOpcStore(ILog Log)
{
LogOpcStore = Log;
}
public async Task OpenOpcConnection()
{
try
{
if ((!string.IsNullOrEmpty(this.opcServerName) & (this.opcServerName != AppMain.MyAppSettings.OpcServer)) ||
(!string.IsNullOrEmpty(this.opcUserId) & (this.opcUserId != AppMain.MyAppSettings.OpcUserId)) ||
(!string.IsNullOrEmpty(this.opcPassword) & (this.opcPassword != AppMain.MyAppSettings.OpcPassword)))
{
await channel.CloseAsync();
this.opcServerName = AppMain.MyAppSettings.OpcServer;
this.opcUserId = AppMain.MyAppSettings.OpcUserId;
this.opcPassword = AppMain.MyAppSettings.OpcPassword;
}
if (channel==null || (channel != null && (channel.State == CommunicationState.Closed || channel.State == CommunicationState.Faulted)))
{
var appDescription = new ApplicationDescription()
{
ApplicationName = "MyAppName",
ApplicationUri = $"urn:{System.Net.Dns.GetHostName()}:MyAppName",
ApplicationType = ApplicationType.Client,
};
//application data won't be deleted when uninstall
var certificateStore = new DirectoryStore(
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), MyAppName", "pki"),
true, true
);
//if the Ethernet cable unplugs or the Wifi drops out,
//you have some timeouts that can keep the session open for a while.
//There is a SessionTimeout (default of 2 min).
this.channel = new UaTcpSessionChannel(
appDescription,
certificateStore,
SignInOpc,
AppMain.MyAppSettings.OpcServer,
null,
options: new UaTcpSessionChannelOptions { SessionTimeout = 120000 });
await channel.OpenAsync();
//LogOpcStore.Info(String.Format("Opc connection sucessful"));
}
this.opcServerName = AppMain.MyAppSettings.OpcServer;
this.opcUserId = AppMain.MyAppSettings.OpcUserId;
this.opcPassword = AppMain.MyAppSettings.OpcPassword;
}
catch (Exception ex)
{
ServiceException serviceException = new ServiceException(ex.HResult + " " + ex.Message, "C052");
throw serviceException;
}
}
private static async Task RecursivelyFindNode(UaTcpSessionChannel channel, NodeId nodeid)
{
BrowseRequest browseRequest = new BrowseRequest
{
NodesToBrowse = new BrowseDescription[] { new BrowseDescription { NodeId = nodeid, BrowseDirection = BrowseDirection.Forward, ReferenceTypeId = NodeId.Parse(ReferenceTypeIds.HierarchicalReferences), NodeClassMask = (uint)NodeClass.Variable | (uint)NodeClass.Object, IncludeSubtypes = true, ResultMask = (uint)BrowseResultMask.All } },
};
BrowseResponse browseResponse = await channel.BrowseAsync(browseRequest);
foreach (var rd1 in browseResponse.Results[0].References ?? new ReferenceDescription[0])
{
uint chid = AppMain.MyTagDatabase.GetClientHandleByTag(rd1.DisplayName.ToString());
if (chid > 0)
{
AppMain.MyTagDatabase.UpdateNodeByClientHandle(chid, rd1.NodeId.ToString());
}
await RecursivelyFindNode(channel, ExpandedNodeId.ToNodeId(rd1.NodeId, channel.NamespaceUris));
}
}
public async Task CreateSubscription(DataContainer dc)
{
double curReadingValue;
try
{
dataCointainer = dc;
await RecursivelyFindNode(channel, NodeId.Parse(ObjectIds.RootFolder));
if (AppMain.MyTagDatabase.GetCntTagsNotInOpcServer() == AppMain.MyTagDatabase.GetTagCount())
{
//no need to create subscription
return;
}
//subscription timeout that is the product of PublishingInterval * LifetimeCount:
var subscriptionRequest = new CreateSubscriptionRequest
{
RequestedPublishingInterval = 1000f,
RequestedMaxKeepAliveCount = 30,
RequestedLifetimeCount = 30 * 3,
PublishingEnabled = true,
};
var subscriptionResponse = await channel.CreateSubscriptionAsync(subscriptionRequest);
id = subscriptionResponse.SubscriptionId;
var itemsToCreate = new MonitoredItemCreateRequest[AppMain.MyTagDatabase.GetTagHavingNodeCount()];
int i = 0;
foreach (var item in AppMain.MyTagDatabase.GetMyTagDatabase())
{
var itemKey = item.Key;
var itemValue = item.Value;
itemsToCreate[i] = new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse(itemValue.NodeId), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = itemKey, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true } };
i++;
}
var itemsRequest = new CreateMonitoredItemsRequest
{
SubscriptionId = id,
ItemsToCreate = itemsToCreate,
};
var itemsResponse = await channel.CreateMonitoredItemsAsync(itemsRequest);
token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(pr =>
{
// loop thru all the data change notifications
// receiving data change notifications here
var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>();
foreach (var dcn in dcns)
{
foreach (var min in dcn.MonitoredItems)
{
MyTag MyTag = new MyTag();
bool hasValue = AppMain.MyTagDatabase.GetMyTag(min.ClientHandle, out MyTag);
if (hasValue)
{
if (double.TryParse(min.Value.Value.ToString(), out curReadingValue))
{
//LogOpcStore.Info(String.Format("ClientHandle : {0} TagName : {1} SourceTimestamp : {2} ServerTimeStamp : {3} curReadingValue : {4}", min.ClientHandle, MyTag.TagName, min.Value.SourceTimestamp, min.Value.ServerTimestamp, curReadingValue));
AddDataPointToContainer(1, MyTag.TagName, min.Value.SourceTimestamp, curReadingValue);
}
}
}
}
});
}
catch (Exception ex)
{
//If the interruption lasts longer than these timeouts then the SessionChannel and Subscriptions will need to be recreated.
channel = null;
FatalServiceException fatalserviceException = new FatalServiceException(ex.Message, "C052");
throw fatalserviceException;
}
}
public async Task DeleteSubscription()
{
try
{
var request = new DeleteSubscriptionsRequest
{
SubscriptionIds = new uint[] { id }
};
await channel.DeleteSubscriptionsAsync(request);
token.Dispose();
}
catch (Exception ex)
{
ServiceException serviceException = new ServiceException(ex.Message, "C052");
throw serviceException;
}
}
private static async Task<IUserIdentity> SignInOpc(EndpointDescription endpoint)
{
IUserIdentity userIdentity = null;
if (endpoint.UserIdentityTokens.Any(p => p.TokenType == UserTokenType.Anonymous))
{
userIdentity = new AnonymousIdentity();
}
else if (endpoint.UserIdentityTokens.Any(p => p.TokenType == UserTokenType.UserName))
{
var userName = AppMain.MyAppSettings.OpcUserId;
var password = simplerAES.Decrypt(AppMain.MyAppSettings.OpcPassword);
userIdentity = new UserNameIdentity(userName, password);
}
return userIdentity;
}
private void AddDataPointToContainer(int dataType, string source, DateTime SourceTimestampUTC, double value)
{
ConditionValue conditionValue = new ConditionValue();
long timestamp = AppMain.ServerSyncTimeStore.ConvertDateTimeToTimeStampUTC(SourceTimestampUTC);
conditionValue.dataType = dataType;
conditionValue.source = source;
conditionValue.timestamp = timestamp;
conditionValue.SourceTimestampUTC = SourceTimestampUTC;
conditionValue.LocalTime = SourceTimestampUTC.ToLocalTime();
conditionValue.value = value;
//LogOpcStore.Info(String.Format("TagName : {0} SourceTimestampUTC : {1} timestamp : {2} LocalTime : {3} curReadingValue : {4}", source, SourceTimestampUTC, timestamp, SourceTimestampUTC.ToLocalTime(), value));
dataCointainer.AddDataPoint(conditionValue);
}
}
I see you are using the project https://github.com/convertersystems/opc-ua-client.
When a server closes the session and socket (as happens when you reinitialize Kepware) the client receives immediate notification that causes the client channel to fault. A faulted channel cannot be reopened, it should be aborted and a new channel should be created.
I made this standalone test, to show that you may have to catch an exception and recreate the channel and subscription. The point of this test is to subscribe to the CurrentTime node and collect 60 datachanges. The test should last a minute. If you re-init the Kepware server in the middle of the test, the code catches the exception and recreates the channel and subscription.
[TestMethod]
public async Task OpcConnectorTest()
{
var count = 0;
UaTcpSessionChannel channel = null;
while (count < 60)
{
try
{
channel = new UaTcpSessionChannel(
this.localDescription,
this.certificateStore,
new AnonymousIdentity(),
EndpointUrl,
SecurityPolicyUris.None,
loggerFactory: this.loggerFactory);
await channel.OpenAsync();
// create the keep alive subscription.
var subscriptionRequest = new CreateSubscriptionRequest
{
RequestedPublishingInterval = 1000f,
RequestedMaxKeepAliveCount = 30,
RequestedLifetimeCount = 30 * 3,
PublishingEnabled = true,
};
var subscriptionResponse = await channel.CreateSubscriptionAsync(subscriptionRequest).ConfigureAwait(false);
var id = subscriptionResponse.SubscriptionId;
var token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(pr =>
{
// loop thru all the data change notifications
var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>();
foreach (var dcn in dcns)
{
foreach (var min in dcn.MonitoredItems)
{
Console.WriteLine($"sub: {pr.SubscriptionId}; handle: {min.ClientHandle}; value: {min.Value}");
count++;
}
}
});
var itemsRequest = new CreateMonitoredItemsRequest
{
SubscriptionId = id,
ItemsToCreate = new MonitoredItemCreateRequest[]
{
new MonitoredItemCreateRequest { ItemToMonitor = new ReadValueId { NodeId = NodeId.Parse("i=2258"), AttributeId = AttributeIds.Value }, MonitoringMode = MonitoringMode.Reporting, RequestedParameters = new MonitoringParameters { ClientHandle = 12345, SamplingInterval = -1, QueueSize = 0, DiscardOldest = true } }
},
};
var itemsResponse = await channel.CreateMonitoredItemsAsync(itemsRequest);
while (channel.State == CommunicationState.Opened && count < 60)
{
await Task.Delay(1000);
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.GetType()}. {ex.Message}");
}
}
if (channel != null)
{
Console.WriteLine($"Closing session '{channel.SessionId}'.");
await channel.CloseAsync();
}
}
I know this is an old post, but I stumbled upon this problem as well. For those interested:
The problem is related to the subscription(s).
When the following code is run:
token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(pr =>
{
// loop thru all the data change notifications
// receiving data change notifications here
var dcns = pr.NotificationMessage.NotificationData.OfType<DataChangeNotification>();
foreach (var dcn in dcns)
{
foreach (var min in dcn.MonitoredItems)
{
MyTag MyTag = new MyTag();
bool hasValue = AppMain.MyTagDatabase.GetMyTag(min.ClientHandle, out MyTag);
if (hasValue)
{
if (double.TryParse(min.Value.Value.ToString(), out curReadingValue))
{
//LogOpcStore.Info(String.Format("ClientHandle : {0} TagName : {1} SourceTimestamp : {2} ServerTimeStamp : {3} curReadingValue : {4}", min.ClientHandle, MyTag.TagName, min.Value.SourceTimestamp, min.Value.ServerTimestamp, curReadingValue));
AddDataPointToContainer(1, MyTag.TagName, min.Value.SourceTimestamp, curReadingValue);
}
}
}
}
});
Observable.subscribe() takes multiple arguments. You should include what to do in case of an error. For example:
token = channel.Where(pr => pr.SubscriptionId == id).Subscribe(
pr => { code to run normally... },
ex => { Log.Info(ex.Message); },
() => { }
);
See http://reactivex.io/documentation/operators/subscribe.html for more information.

Add OR condition to query

I am wondering how it is possible to add an OR condition to the Envers criteria api:
public IEnumerable<Guid> GetHistory(object id, params string[] props)
{
var auditQuery = AuditReaderFactory.Get(Session).CreateQuery()
.ForRevisionsOfEntity(typeof(T), false, true);
foreach (var prop in props)
{
auditQuery.Add(AuditEntity.RelatedId(prop).Eq(id)); // <-- adds AND, while OR is required!
}
return auditQuery
.GetResultList<object[]>()
.Select(i => ((T)i[0]).ID)
.Distinct();
}
Use AuditEntity.Disjunction().
In your example, something like...
[..]
var disjunction = AuditEntity.Disjunction();
foreach (var prop in props)
{
disjunction.Add(AuditEntity.RelatedId(prop).Eq(id));
}
auditQuery.Add(disjunction);
[..]
I did like this in Java as #Roger mentioned above. (Just in case if anybody needs)
public List<Employee> getAuditHistory(Session session, int id, String property) {
AuditReader auditReader = AuditReaderFactory.get(session);
List<Employee> employeeHistory = new ArrayList<>();
if (auditReader != null) {
AuditQuery auditQuery = auditReader.createQuery().forRevisionsOfEntity(Employee.class, true, false)
.add(AuditEntity.property(ResultsConstants.Employee_ID).eq(id));
AuditDisjunction auditDisjunction = null;
if (property.equalsIgnoreCase("FULL_NAME")) {
auditDisjunction = AuditEntity.disjunction().add(AuditEntity.property("FIRST_NAME".toUpperCase()).hasChanged())
.add(AuditEntity.property("LAST_NAME".toUpperCase()).hasChanged());
} else {
auditQuery = auditQuery.add(AuditEntity.property(property.toUpperCase()).hasChanged());
}
auditQuery = auditQuery.addOrder(AuditEntity.property("MODIFIED_DATE").desc());
if(null != auditDisjunction){
auditQuery = auditQuery.add(auditDisjunction);
}
if (auditQuery != null) {
if (auditQuery.getResultList().isEmpty()) {
// Log here or throw it back to caller
}
employeeHistory.addAll(auditQuery.getResultList());
}
}
return employeeHistory;
}