<commit_before>﻿namespace LinqToSalesforce

open System
open System.Reflection
open Rest
open Rest.OAuth
open System.Collections
open System.Collections.Generic
open System.Linq
open Entities
open Translator
open Visitor

module private ContextHelper =
  let execute<'t when 't :> ISalesforceEntity>(client:Client) (tracker:Tracker) operations tableName =
    let typ = typeof<'t>
    let soql = buildSoql operations typ tableName
    //printfn "SOQL: %s" soql
    let rs = client.ExecuteSoql<'t> soql |> Async.RunSynchronously
    match rs with
    | Success r ->
      let records = r.Records.ToList()
      for r in records do
        r.PropertyChanged.Add
          <| fun _ -> tracker.Track r
      records
    | Failure [e] -> e.ToException() |> raise
    | Failure errors -> 
        errors 
          |> List.map (fun e -> e.ToException() :> Exception) 
          |> List.toArray
          |> AggregateException
          |> raise

type RelationShip<'tp,'tc
  when 'tp :> ISalesforceEntity 
  and 'tc :> ISalesforceEntity>(client:Client, referenceField:string, tracker:Tracker, parent:'tp) =

  let loadResults () =
    let childType = typeof<'tc>
    let chidlTableName = childType.Name
    let field = { Name=referenceField; DecorationName=None; Type=typeof<string> }
    let cmp = { Field=field; Kind=ComparisonKind.Equal; Target=(Constant(parent.Id)) }
    let operations = 
      [ Select (SelectType childType)
        Where (UnaryComparison(cmp)) ]
    let results = ContextHelper.execute<'tc> client tracker operations chidlTableName
    for r in results do
      RelationShip<'tp,_>.Build childType client tracker r
    results
  let results = lazy (loadResults ())
  member __.Results () =
    results.Value
  interface IEnumerable<'tc> with
    member x.GetEnumerator(): IEnumerator = 
      results.Value.GetEnumerator() :> IEnumerator
    member x.GetEnumerator(): IEnumerator<'tc> = 
      results.Value.GetEnumerator() :> IEnumerator<'tc>

  static member Build (typ:Type) (client:Client) (tracker:Tracker) (parent:#ISalesforceEntity) =
    let td = typedefof<RelationShip<_,_>>
    typ.GetProperties() 
      |> Seq.filter (
          fun p -> 
            p.PropertyType.IsGenericType && (p.PropertyType.GetGenericTypeDefinition() = td)
          )
      |> Seq.iter (
          fun r1 ->
            let ct = parent.GetType()
            let co = r1.PropertyType.GetConstructor([|typeof<Client>; typeof<string>; typeof<Tracker>; ct|])
            let referencedByFieldAttr = r1.GetCustomAttributes<ReferencedByFieldAttribute>() |> Seq.head
            let instance = co.Invoke([|client; referencedByFieldAttr.Name; tracker; parent|])
            r1.SetValue(parent, instance)
         )

type SoqlQueryContext<'t when 't :> ISalesforceEntity>(client:Client, tracker:Tracker) =
  interface IQueryContext with
    member x.Execute expression _ =
      let visitor = new RequestExpressionVisitor(expression)
      visitor.Visit()
      let operations = visitor.Operations |> Seq.toList
      let typ = typeof<'t>
      let tableName = typ.Name
      let results = ContextHelper.execute<'t> client tracker operations tableName
      for r in results do
        RelationShip<'t,_>.Build typ client tracker r
      results :> obj

type SoqlContext (instanceName:string, authparams:ImpersonationParam) =
  let client = Client(instanceName, authparams)
  let tracker = new Tracker()
  
  member x.GetTable<'t when 't :> ISalesforceEntity>() =
    let c = new SoqlQueryContext<'t>(client, tracker)
    let tableName = typeof<'t>.Name
    let queryProvider = new QueryProvider(c, tableName)
    new Queryable<'t>(queryProvider, tableName)

  member x.Insert entity =
    match client.Insert entity with
    | Success _ ->
        entity.PropertyChanged.Add
          <| fun _ -> tracker.Track entity
    | Failure [e] -> e.ToException() |> raise
    | Failure errors -> 
        errors 
          |> List.map (fun e -> e.ToException() :> Exception) <commit_msg>FEAT: find god table name when building SOQL<commit_after>﻿namespace LinqToSalesforce

open System
open System.Reflection
open Rest
open Rest.OAuth
open System.Collections
open System.Collections.Generic
open System.Linq
open Entities
open Translator
open Visitor

module private ContextHelper =
  let execute<'t when 't :> ISalesforceEntity>(client:Client) (tracker:Tracker) operations tableName =
    let typ = typeof<'t>
    let soql = buildSoql operations typ tableName
    //printfn "SOQL: %s" soql
    let rs = client.ExecuteSoql<'t> soql |> Async.RunSynchronously
    match rs with
    | Success r ->
      let records = r.Records.ToList()
      for r in records do
        r.PropertyChanged.Add
          <| fun _ -> tracker.Track r
      records
    | Failure [e] -> e.ToException() |> raise
    | Failure errors -> 
        errors 
          |> List.map (fun e -> e.ToException() :> Exception) 
          |> List.toArray
          |> AggregateException
          |> raise

type RelationShip<'tp,'tc
  when 'tp :> ISalesforceEntity 
  and 'tc :> ISalesforceEntity>(client:Client, referenceField:string, tracker:Tracker, parent:'tp) =

  let loadResults () =
    let childType = typeof<'tc>
    let chidlTableName = findEntityName childType
    let field = { Name=referenceField; DecorationName=None; Type=typeof<string> }
    let cmp = { Field=field; Kind=ComparisonKind.Equal; Target=(Constant(parent.Id)) }
    let operations = 
      [ Select (SelectType childType)
        Where (UnaryComparison(cmp)) ]
    let results = ContextHelper.execute<'tc> client tracker operations chidlTableName
    for r in results do
      RelationShip<'tp,_>.Build childType client tracker r
    results
  let results = lazy (loadResults ())
  member __.Results () =
    results.Value
  interface IEnumerable<'tc> with
    member x.GetEnumerator(): IEnumerator = 
      results.Value.GetEnumerator() :> IEnumerator
    member x.GetEnumerator(): IEnumerator<'tc> = 
      results.Value.GetEnumerator() :> IEnumerator<'tc>

  static member Build (typ:Type) (client:Client) (tracker:Tracker) (parent:#ISalesforceEntity) =
    let td = typedefof<RelationShip<_,_>>
    typ.GetProperties() 
      |> Seq.filter (
          fun p -> 
            p.PropertyType.IsGenericType && (p.PropertyType.GetGenericTypeDefinition() = td)
          )
      |> Seq.iter (
          fun r1 ->
            let ct = parent.GetType()
            let co = r1.PropertyType.GetConstructor([|typeof<Client>; typeof<string>; typeof<Tracker>; ct|])
            let referencedByFieldAttr = r1.GetCustomAttributes<ReferencedByFieldAttribute>() |> Seq.head
            let instance = co.Invoke([|client; referencedByFieldAttr.Name; tracker; parent|])
            r1.SetValue(parent, instance)
         )

type SoqlQueryContext<'t when 't :> ISalesforceEntity>(client:Client, tracker:Tracker) =
  interface IQueryContext with
    member x.Execute expression _ =
      let visitor = new RequestExpressionVisitor(expression)
      visitor.Visit()
      let operations = visitor.Operations |> Seq.toList
      let typ = typeof<'t>
      let tableName = findEntityName typ
      let results = ContextHelper.execute<'t> client tracker operations tableName
      for r in results do
        RelationShip<'t,_>.Build typ client tracker r
      results :> obj

type SoqlContext (instanceName:string, authparams:ImpersonationParam) =
  let client = Client(instanceName, authparams)
  let tracker = new Tracker()
  
  member x.GetTable<'t when 't :> ISalesforceEntity>() =
    let c = new SoqlQueryContext<'t>(client, tracker)
    let tableName = typeof<'t>.Name
    let queryProvider = new QueryProvider(c, tableName)
    new Queryable<'t>(queryProvider, tableName)

  member x.Insert entity =
    match client.Insert entity with
    | Success _ ->
        entity.PropertyChanged.Add
          <| fun _ -> tracker.Track entity
    | Failure [e] -> e.ToException() |> raise
    | Failure errors -> 
        errors 
          |> List.map (fun e -> e.ToException() :> Exception) 
{'fraction_non_alphanumeric': 0.10630855715178014, 'fraction_numerical': 0.0009993753903810119, 'mean_word_length': 3.0805300713557595, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 121, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2746333', 'n_tokens_mistral': 2362, 'n_tokens_neox': 2337, 'n_words': 613}