Giter VIP home page Giter VIP logo

editor-net's People

Contributors

allan-test avatar allanjard avatar dependabot[bot] avatar fbognini avatar gotmoo avatar sandydatatables avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

editor-net's Issues

License file for .NET Library

Hi @AllanJard !

First off, thanks for DataTables and deciding to open source it and the server-side libraries!

However, I can't seem to find a License file detailing the license used by the server-side libraries.

Can you please point me where it is?

Thanks!
cc @TomeCirun @tino097

Feature Request: Allow set() to take flags.

This does not work:

.Field(new Field("InventoryServer.UpdatedByDisplayName")
    .Set(Field.SetType.Create | Field.SetType.Edit) // <--------------------
    .SetValue("TODO"))

I think theres a lot of value here. Otherwise I have to do this in an event.

SearchPanes and SearchBuilder do not consider pre-filtering of the table.

When you apply a top level filter on a table, this is not taken into account by the options presented by SearchBuilderOptions or SearchPaneOptions.

Setup

Using the current example download for Editor. Showing this with SearchPanes because it is easier to see in the browser:

  1. Modify Controllers\SearchPanesController.cs to add a top level filter on site using the Where clause
var response = new Editor(db, "users")
    .Model<UploadManyModel>()
    .Where("site", "1", "=")
  1. Run the project and visit /examples/extensions/searchPanes.html

Result

The resulting table shows the correct data set:
image

However, the SearchPanes options still show the options from the full data set:
image

This includes the other options like Name:
image

Selecting one of the pre-filtered names results in zero records found, while the options show there should be one.

Expected behavior

Pre-filtered items are hidden from the SearchPane/SearchBuilder options.

This could result in data leaking, or weird results from logically deleted records.

Async methods?

For example Validate Async?

IE:

var editor = new Editor(db, "FieldDefinition", "Id")
    .ValidatorAsync(async (editor, action, data) =>
    {
        // ...
        var hasPermission = await policyServerRuntimeClient.HasPermissionAsync(this.User, "WriteInventory");
    })

SearchBuilderOptions()/SearchPaneOptions() include unrelated Left Joins

In the samples SearchBuilderController.cs adding addition joins show up in that search builder query:

      .Field(new Field("users.site")
          .Options(new Options()
              .Table("sites")
              .Value("id")
              .Label("name")
          )
      )
      .Field(new Field("sites.name")
          .SearchBuilderOptions(new SearchBuilderOptions()
              .Label("sites.name")
              .Value("sites.name")
              .LeftJoin("sites", "sites.id", "=", "users.site")
          )
      )
    .LeftJoin("sites", "sites.id", "=", "users.site")
    .LeftJoin("user_dept", "users.id", "=", "user_dept.user_id")
SELECT  sites.name as 'value', sites.name as 'label' 
FROM  users 
LEFT JOIN sites ON sites.id = users.site  
LEFT JOIN user_dept ON users.id = user_dept.user_id  
GROUP BY sites.name

Should global validators run AFTER field validators?

Maybe im approaching this wrong but I have a global validator that needs to do some special custom checks.

Because my global validator runs first im duplicating a ton of field validators:

// The entire purpose of this validator is to check if the FieldDefinition already exists associated to the same entities
// For example a field definition for Name cannot exist twice both added to InventoryServers
.Validator((editor, action, data) =>
{
    if (action == DtRequest.RequestTypes.DataTablesGet)
    {
        return string.Empty;
    }

    foreach (var pair in data.Data)
    {
        var values = pair.Value as Dictionary<string, object>;

        if (!values.TryGetValue("FieldDefinition", out var fieldDefinitionData))
        {
            // DUPLICATED FIELD VALIDATOR
            return string.Empty;
        }
        else
        {
            if (action is DtRequest.RequestTypes.EditorEdit
                or DtRequest.RequestTypes.EditorCreate)
            {
                var fieldDefinitionDictionary = fieldDefinitionData as Dictionary<string, object>;
                if (!fieldDefinitionDictionary.TryGetValue("Name", out var fieldDefinitionNameData))
                {
                    // DUPLICATED FIELD VALIDATOR
                    // An edit does not have to result in a name change (they didnt send Name field).
                    // A create Name is required, so another validator will catch this
                    return string.Empty;
                }

                var fieldDefinitionName = fieldDefinitionNameData.ToString();
                if (string.IsNullOrWhiteSpace(fieldDefinitionName))
                {
                    // DUPLICATED FIELD VALIDATOR
                    // Field validator will catch it
                    return string.Empty;
                }

                if (!values.TryGetValue("FieldDefinitionEntity", out var entities))
                {
                    // DUPLICATED FIELD VALIDATOR
                    return string.Empty;
                }

                if (action is DtRequest.RequestTypes.EditorEdit)
                {
                    var existingFieldDefinition = context.WuitFieldDefinition
                        .Include(x => x.Entities)
                        .FirstOrDefault(x => x.Id == Convert.ToInt32(pair.Key, CultureInfo.InvariantCulture));
                    if (fieldDefinitionName == existingFieldDefinition.Name)
                    {
                        // Name did not change, no need to check if it exists
                        continue;
                    }
                }

                var entitiesDict = entities as Dictionary<string, object>;
                var entityIds = entitiesDict.Values
                    .Select(x => x as Dictionary<string, object>)
                    .Select(x => (int)x["Id"])
                    .ToArray();

                if (entityIds.Count == 0)
                {
                    // DUPLICATED FIELD VALIDATOR
                    // Field validator for the MJoin below will check that they provided a min of 1
                    return string.Empty;
                }

                var existingRecord = IsFieldDefinitionInUse(fieldDefinitionName, entityIds);

                if (existingRecord is null)
                {
                    return string.Empty;
                }

                return $"Field Definition already exists for field name {fieldDefinitionName} ({existingRecord.Id}) on entities {string.Join(", ", existingRecord.Entities.Select(x => x.Name))}";
            }
        }
    }

    return string.Empty;
})

Now this would be my code if I could trust that field validators all ran and that it was safe to access my dictionary:

.Validator((editor, action, data) =>
{
    if (action == DtRequest.RequestTypes.DataTablesGet)
    {
        return string.Empty;
    }

    // Create/Edit can accept multiple records at once
    foreach (var pair in data.Data)
    {
        var values = pair.Value as Dictionary<string, object>;

        if (!values.TryGetValue("FieldDefinition", out var fieldDefinitionData))
        {
            return string.Empty;
        }
        else
        {
            if (action is DtRequest.RequestTypes.EditorEdit or DtRequest.RequestTypes.EditorCreate)
            {
                var fieldDefinitionDictionary = fieldDefinitionData as Dictionary<string, object>;
                var fieldDefinitionName = fieldDefinitionDictionary["Name"].ToString();
                var entities = values["FieldDefinitionEntity"] as Dictionary<string, object>;
                var entityIds = entities.Values
                    .Select(x => x as Dictionary<string, object>)
                    .Select(x => (int)x["Id"])
                    .ToArray();

                var existingFieldDefinition = context.WuitFieldDefinition
                    .Include(x => x.Entities)
                    .FirstOrDefault(x => x.Id == Convert.ToInt32(pair.Key, CultureInfo.InvariantCulture) &&
                    x.Name == fieldDefinitionName);

                if (existingFieldDefinition is not null)
                {
                    // Name did not change, no need for further checks
                    continue;
                }

                var existingRecord = IsFieldDefinitionInUse(fieldDefinitionName, entityIds);
                if (existingRecord is null)
                {
                    return string.Empty;
                }

                return $"Field Definition already exists for field name {fieldDefinitionName} ({existingRecord.Id}) on entities {string.Join(", ", existingRecord.Entities.Select(x => x.Name))}";
            }
        }
    }

    return string.Empty;
})

Looking at the code looks like PreX events run before field validation too... so those are not a workaround for me either.

Cosmos Db

Hello, this is more of a feature request

I've stumbled upon this packages through the datatables.net site and saw it mostly supports relational Databases.

Any chance this gets support for Cosmos Db? It could leverages the OData functionality to create complex filters?

MJoin with inner LeftJoin, does not put LeftJoin at end.

RE: https://datatables.net/forums/discussion/78882/editor-net-mjoin-leftjoin#latest

I think this is a legit bug.

// ...
  .MJoin(new MJoin("InventoryServerWarranty")
      .Set(false)
      .Link("InventoryServer.Id", "InventoryServerInventoryServerWarrantyLine.InventoryServerId")
      .Link("InventoryServerWarranty.Id", "InventoryServerInventoryServerWarrantyLine.InventoryServerWarrantyId")
      .Order("InventoryServerWarranty.CertificateNumber")
      .Model<Warranty>()
      .LeftJoin("InventoryServerWarrantyProvider", "InventoryServerWarrantyProvider.Id", "=", "InventoryServerWarranty.InventoryServerWarrantyProviderId")
  )
// ...

The Editor generated SQL looks like so:

SELECT
    DISTINCT [InventoryServer].[Id] as 'dteditor_pkey',
    [InventoryServerWarranty].[CertificateNumber] as 'CertificateNumber',
    [InventoryServerWarranty].[RegistrationID] as 'RegistrationID',
    [InventoryServerWarranty].[Notes] as 'Notes',
    [InventoryServerWarranty].[DeliveryDate] as 'DeliveryDate',
    [InventoryServerWarranty].[ExpirationDate] as 'ExpirationDate',
    [InventoryServerWarranty].[InventoryServerWarrantyProviderId] as 'InventoryServerWarrantyProviderId',
    [InventoryServerWarranty].[RetiredIncidentNumber] as 'RetiredIncidentNumber',
    [InventoryServerWarranty].[RetiredOn] as 'RetiredOn'
FROM
    [InventoryServer]
    LEFT JOIN [InventoryServerWarrantyProvider] ON [InventoryServerWarrantyProvider].[Id] = [InventoryServerWarranty].[InventoryServerWarrantyProviderId]
    JOIN [InventoryServerInventoryServerWarrantyLine] ON [InventoryServer].[Id] = [InventoryServerInventoryServerWarrantyLine].[InventoryServerId]
    JOIN [InventoryServerWarranty] ON [InventoryServerWarranty].[Id] = [InventoryServerInventoryServerWarrantyLine].[InventoryServerWarrantyId]
WHERE
    [InventoryServer].[Id] IN (2)
ORDER BY
    [InventoryServerWarranty].[CertificateNumber]

Note how that left join is FIRST. That will not work. Only by pulling that down to the be the final join, does the code work.

Comments seem to lie a lot

Sample project JoinArrayController says:

    /// This example shows a very simple join using the `LeftJoin` method.
    /// Of particular note in this example is that the `JoinModel` defines two
    /// nested classes that obtain the data required from the two tables

JoinModelUsers and JoinModelSites are not nested classes:

using DataTables;

namespace EditorNetCoreDemo.Models
{
    public class JoinModelUsers
    {
        public string first_name { get; set; }

        public string last_name { get; set; }

        public string phone { get; set; }

        public int site { get; set; }

        public int manager { get; set; }
    }

    public class JoinModelSites
    {
        public string name { get; set; }
    }
}

I am trying to mirror the examples and get my code working and getting an "Object is not set to an instance of an object" error with no-stack trace on a simple LeftJoin and am struggling.

MJoin Model does not support nested types.

https://github.com/DataTables/Editor-NET/blob/master/DataTables-Editor-Server/MJoin.cs#L750

VS

https://github.com/DataTables/Editor-NET/blob/master/DataTables-Editor-Server/Editor.cs#L1394

Issue:

https://datatables.net/forums/discussion/78941/editor-net-mjoin-leftjoin-data#latest

This forces us to define the nested fields manually via:

.MJoin(new MJoin("InventoryServerWarranty")
    .Model<InventoryServerWarranty>()
    .Field(new Field("InventoryServerWarrantyProvider.Name"))
    .Field(new Field("InventoryServerWarrantyProvider.Description"))

SearchBuilderOptions()/SearchPaneOptions() generated invalid code on SqlServer

Modify the sample to the following code

.Field(new Field("sites.name")
    .SearchBuilderOptions(new SearchBuilderOptions()
        .Value("sites.id")
        .Label("sites.name")
        .LeftJoin("sites", "sites.id", "=", "users.site")
    )
)

This works on SQLite not SqlServer/MSSQL.

With SqlServer, you will get "Column '[sites].[name]' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."

Finally, "fixing" this is:

.Field(new Field("sites.name")
    .SearchBuilderOptions(new SearchBuilderOptions()
        .Value("sites.id")
        .Label("MAX(sites.name)")
        .LeftJoin("sites", "sites.id", "=", "users.site")
    )
)

Not sure if that is the appropriate fix though...

"Incorrect syntax near 'OFFSET'. Invalid usage of the option NEXT in the FETCH statement"

I get this error when I click a column sort 3 times. It sorts ASC on first click, the DESC, then error.

The generated SQL is:

SELECT
    [InventoryServer].[Id] as 'InventoryServer.Id',
    [InventoryServer].[Name] as 'InventoryServer.Name',
    [InventoryServer].[DockDate] as 'InventoryServer.DockDate',
    [InventoryServer].[BillingStartDate] as 'InventoryServer.BillingStartDate',
    [PatchPartyDowntime].[Name] as 'PatchPartyDowntime.Name',
    [VirtualEnvironment].[Description] as 'VirtualEnvironment.Description'
    -- Many more redacted
FROM
    [InventoryServer]
    LEFT JOIN [ContactGroup] ON [ContactGroup].[Id] = [InventoryServer].[ContactGroupId]
    LEFT JOIN [DisasterRecoveryTeir] ON [DisasterRecoveryTeir].[Id] = [InventoryServer].[DisasterRecoveryTeirId]
    LEFT JOIN [Domain] ON [Domain].[Id] = [InventoryServer].[DomainId]
    -- Many more redacted
WHERE
    (1 = 1) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY

It appears the order by is ripped away as a part of removing the sort on my third click.

Let me know if you need more code.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.