Swashbuckle Schema Filter - How to get property names? - swashbuckle

I am writing a schema filter for Swashbuckle to implement oneOf/allOf support for properties. As part of this effort, I need to get the translation of property names from the c# class definition to what is stored in the schema. Currently, I'm doing a case-insensitive match and then checking for a JsonPropertyName attribute...
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
foreach (var prop in context.Type.GetProperties())
{
var propName = prop.Name;
var schemaPropName = schema.Properties.Keys.FirstOrDefault(k => String.Compare(propName, k, true) == 0);
if (schemaPropName != null)
{
propName = schemaPropName;
}
var attrJsonName = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
if (attrJsonName != null)
{
var altName = attrJsonName.Name;
if (!string.IsNullOrEmpty(altName))
{
propName = altName;
}
}
// etc.
but that seem brittle. For example, what if there is a custom naming convention that inserts underscores in property names? What if there are other name-changing filters? etc.
Any ideas on better ways to do this? Is there some mechanism I can use in either OpenApiSchema or SchemaFilterContext to map c# property names to schema property names?

Related

How can I distinct a complex object list in DART

I have one list of complex object. How can I distinct the list using their IDs?
I cant use toSet and similars, because the hashcode from the objects all are diferent.
1) Vanilla Dart
Loop through the list, adding IDs to a set as you go. Whenever you add an ID to the set that didn't already exist, add that element to a new list of distinct values.
void main() {
var list = [
Data('a'),
Data('a'),
Data('b'),
Data('c'),
];
var idSet = <String>{};
var distinct = <Data>[];
for (var d in list) {
if (idSet.add(d.id)) {
distinct.add(d);
}
}
}
class Data {
Data(this.id);
final String id;
}
2) Packages
Several packages exist that expand on default the Iterable utility methods, such as flinq or darq. They add a distinct method you can call to easily get a list of unique members of a list based on some property of the members.
import 'package:darq/darq.dart';
void main() {
var list = [
Data('a'),
Data('a'),
Data('b'),
Data('c'),
];
var distinct = list.distinct((d) => d.id).toList();
}
(Disclaimer, I am the maintainer of darq.)
Try to use this extension:
extension IterableExtension<T> on Iterable<T> {
Iterable<T> distinctBy(Object getCompareValue(T e)) {
var result = <T>[];
this.forEach((element) {
if (!result.any((x) => getCompareValue(x) == getCompareValue(element)))
result.add(element);
});
return result;
}
}
Using:
var distinctList = someList.distinctBy((x) => x.oid);
Or you can use a hash there.

Microsoft.SharePoint.Client C# getting only User created Lists (and not Document Libraries)

I am trying to retrieve a list of user generated Lists from a specified website. I do not want System generated lists (eg MicroFeed) nor Document Libraries. Using the Microsoft example I have this code:
public static void LoadLists(Microsoft.SharePoint.Client.Web web, List<String> foldersList)
{
var ctx = web.Context;
ListCollection collList = web.Lists;
IEnumerable<List> listInfo = ctx.LoadQuery(
collList.Include(
list => list.Title,
list => list.Fields.Include(
field => field.Title,
field => field.InternalName)));
ctx.ExecuteQuery();
foreach (List oList in listInfo)
{
FieldCollection collField = oList.Fields;
foreach (Microsoft.SharePoint.Client.Field oField in collField)
{
Regex regEx = new Regex("name", RegexOptions.IgnoreCase);
if (regEx.IsMatch(oField.InternalName))
{
Console.WriteLine("List: {0} \n\t Field Title: {1} \n\t Field Internal Name: {2}",
oList.Title, oField.Title, oField.InternalName);
}
}
}
}
However this returns all Lists and Document Libraries (and heaven knows what else). Is there an easy way to just get back the user defined lists? Here is an example of what I would like to get:
And looking at the documentation from Microsoft they seems to use the term list to refer to actual lists (tables) and document libraries (folders). What is the proper nomenclature for getting the list that is really just like an excel spreadsheet of data? Finally, is it possible for lists (tables) to be nested in side a Document Libraries? I can't seem to be able to do this, but I wanted to check since I am new to SharePoint.
Thanks!
So after having to lookup lots of examples (not from Microsoft, thank you) and stepping thru actual responses, here is the code for loading only the Lists and their field columns (not hidden) created by the user. I am sure that this could be optimized/cleaned up (for example not having to run the secondary queries to get List attributes, but it gave me access denied in original query), but it is working for me. Also needs some loving care for try-catches in case things go south.
First a couple of classes to hold the data:
public class SharePointColumn
{
public string Title { get; set; }
public string InternalName { get; set; }
public string TypeAsString { get; set; }
}
public class SharePointLibrary
{
public SharePointLibrary()
{
Columns = new List<SharePointColumn>();
}
public string Title { get; set; }
public Boolean IsList { get; set; } // If true a list, else DocumentLibrary
public List<SharePointColumn> Columns { get; set; }
}
Then the real code.
public static void LoadLists(Microsoft.SharePoint.Client.Web web, List<SharePointLibrary> sharePointLibraries)
{
var ctx = web.Context;
ListCollection collList = web.Lists;
IEnumerable<List> listInfo = ctx.LoadQuery(
collList.Include(
list => list.Title,
list => list.Fields.Include(
field => field.Title,
field => field.InternalName,
field => field.Hidden,
field => field.TypeAsString)));
ctx.ExecuteQuery();
foreach (List oList in listInfo)
{
// Had to add these because trying to add in above query failed
ctx.Load(oList);
ctx.ExecuteQuery();
// 544 Base Template is MicroFeed
if (oList.Hidden == false && oList.IsCatalog == false && (!oList.IsObjectPropertyInstantiated("IsSiteAssetsLibrary") || oList.IsSiteAssetsLibrary == false) &&
oList.BaseType != BaseType.DocumentLibrary && oList.BaseTemplate != 544)
{
FieldCollection collField = oList.Fields;
SharePointLibrary lib = new SharePointLibrary
{
Title = oList.Title,
IsList = true,
Columns = new List<SharePointColumn>()
};
foreach (Microsoft.SharePoint.Client.Field oField in collField)
{
if (!oField.Hidden)
{
SharePointColumn col = new SharePointColumn();
col.Title = oField.Title;
col.InternalName = oField.InternalName;
col.TypeAsString = oField.TypeAsString;
lib.Columns.Add(col);
}
}
sharePointLibraries.Add(lib);
}
}
}

Mongo database: search for text escaping some characters

I'm looking for a way to search text on my MongoDB escaping some characters.
For example:
In the collection contacts there is a document with "john.doe" in field name
{
_id: ID
...
name : "john.doe",
...
}
("john.doe" could be "j.ohndoe" or "j.o.h.n.d.o.e" or "jo.hn.do.e", you name it)
I want to find it searching for "johndoe", not only "john.doe" (ignoring "."). It would be great to use directly findOne.
Is there a way to do this?
Thank you very much :)
I don't think it's possible using purely MongoDB queries. However, you can you MongoDB map reduce to get filtered documents or perform such actions on client side. I know it will not be an elegant solution but at least will work.
Please see https://docs.mongodb.org/manual/core/map-reduce/
Example
function findByKey(key)
{
var keys =
db.contacts.mapReduce(function(){
var re = /\./igm;
var txt = this.name.replace(re,"");
var re = new RegExp(u_name);
if(re.exec(txt)!= null)
{
emit(1, this._id);
}
}, function(k, v){
return {keys: v};
},
{
out:{inline:true},
scope: {u_name:key}
});
if(keys.results.length > 0)
{
var arKeys = keys.results[0].value.keys;
return db.contacts.find({_id:{$in: arKeys}});
}
else
{
return null;
}
};
var data = findByKey("john doe");
After running above script, variable data will hold all documents having "john doe" including j.ohn.doe or john.doe so ignoring all periods.

Glass Mapper RenderLink link description - default text if empty

<li>#if (string.IsNullOrWhiteSpace(topLinks.Target.Text))
{
topLinks.Target.Text = "EMPTY DESCRIPTION";
}
#(RenderLink(topLinks, x => x.Target, isEditable: true))
</li>
I need a way to catch it when a Content Editor has set up a link, but not actually put a Link Description in. At the moment it just renders spaces. The above works, but it's clunky and I need to put it everywhere I use a RenderLink. How do I default the text if it's empty?
I've created an extension method to work around it.
Note that I've extended GlassHtml and not GlassView because you may want to pass a different model type than the one that's used for the view.
namespace ParTech.MvcDemo.Context.Extensions
{
using System;
using System.Linq.Expressions;
using System.Web;
using Glass.Mapper.Sc;
using Glass.Mapper.Sc.Fields;
public static class GlassHtmlExtensions
{
public static HtmlString RenderLinkWithDefaultText<T>(this GlassHtml glassHtml, T model, Expression<Func<T, object>> field, object attributes = null, bool isEditable = true, string defaultText = null)
{
var linkField = field.Compile().Invoke(model) as Link;
if (linkField == null || string.IsNullOrEmpty(linkField.Text))
{
return new HtmlString(glassHtml.RenderLink(model, field, attributes, isEditable, defaultText));
}
return new HtmlString(glassHtml.RenderLink(model, field, attributes, isEditable));
}
}
}
You can now do this in your view:
#(((GlassHtml)this.GlassHtml).RenderLinkWithDefaultText(MyModel, x => x.LinkField, null, true, "Static default text"))
Still a bit hacky because you need to cast the IGlassHtml to GlassHtml, but it works.
If you always have the correct model defined for you view (and thus don't need to specify the model parameter) you could put this extension method on GlassView.

How to use sitecore query in datasource location? (dynamic datasource)

Is it possible to set the datasource location (not the datasource) to be a sitecore query?
What I'm trying to do is to have the sublayout set its datasource location to a folder under the item containing it (current item).
The sublayout datasource location should point to a folder under the current item. So I tried setting the datasource location to query:./Items/* but that did not work.
You don't need the query -- the sublayout datasource location can simply use a relative path. e.g.
./Items
Obviously though, that folder needs to exist already. I've been meaning to blog this code, and it may be overkill but I'll post here since it may help you. The following can be added to the getRenderingDatasource pipeline to create a relative path datasource location if it doesn't exist already. Add it before the GetDatasourceLocation processor.
On the sublayout, you'll want to add a parameter contentFolderTemplate=[GUID] to specify the template of the item that gets created.
public class CreateContentFolder
{
protected const string CONTENT_FOLDER_TEMPLATE_PARAM = "contentFolderTemplate";
public void Process(GetRenderingDatasourceArgs args)
{
Assert.IsNotNull(args, "args");
Sitecore.Data.Items.RenderingItem rendering = new Sitecore.Data.Items.RenderingItem(args.RenderingItem);
UrlString urlString = new UrlString(rendering.Parameters);
var contentFolder = urlString.Parameters[CONTENT_FOLDER_TEMPLATE_PARAM];
if (string.IsNullOrEmpty(contentFolder))
{
return;
}
if (!ID.IsID(contentFolder))
{
Log.Warn(string.Format("{0} for Rendering {1} contains improperly formatted ID: {2}", CONTENT_FOLDER_TEMPLATE_PARAM, args.RenderingItem.Name, contentFolder), this);
return;
}
string text = args.RenderingItem["Datasource Location"];
if (!string.IsNullOrEmpty(text))
{
if (text.StartsWith("./") && !string.IsNullOrEmpty(args.ContextItemPath))
{
var itemPath = args.ContextItemPath + text.Remove(0, 1);
var item = args.ContentDatabase.GetItem(itemPath);
var contextItem = args.ContentDatabase.GetItem(args.ContextItemPath);
if (item == null && contextItem != null)
{
string itemName = text.Remove(0, 2);
//if we create an item in the current site context, the WebEditRibbonForm will see an ItemSaved event and think it needs to reload the page
using (new SiteContextSwitcher(SiteContextFactory.GetSiteContext("system")))
{
contextItem.Add(itemName, new TemplateID(ID.Parse(contentFolder)));
}
}
}
}
}
}