Get MVC Bundle Querystring - c#

Is it possible to detect a bundle querystring in ASP.NET MVC?
For example if I have the following bundle request:
/css/bundles/mybundle.css?v=4Z9jKRKGzlz-D5dJi5VZtpy4QJep62o6A-xNjSBmKwU1
Is it possible to extract the v querystring?:
4Z9jKRKGzlz-D5dJi5VZtpy4QJep62o6A-xNjSBmKwU1
I've tried doing this in a bundle transform, but with no luck. I found that even with UseServerCache set to false the transform code didn't always run.

It's been a while since I've worked with the ASP Bundler (I remember it being terrible), and these notes are from my memory. Please verify that it's still valid.
Hopefully this will provide a starting point for your search.
To tackle this problem you'll want to explore around in System.Web.Optimization namespace.
Of most importance is the System.Web.Optimization.BundleResponse class, which has a method named GetContentHashCode() which is exactly what you want. Unfortunately, MVC Bundler has a bad architecture and I'm willing to bet that this is still an internal method. This means you won't be able to call it from your code.
Update
Thanks for the verification. So it looks like you have a few ways of accomplishing your goal:
Compute the hash your self using the same algorithm as ASP Bundler
Use reflection to call into the internal method of the Bundler
Get the URL from bundler (there is a public method for this I believe) and extract the query string, then extract the hash from that (using any string extraction methods)
Get angry at Microsoft for bad design
Lets go with #2 (Be careful, since its marked as internal and not part of the public API, a rename of the method by the Bundler team will break things)
//This is the url passed to bundle definition in BundleConfig.cs
string bundlePath = "~/bundles/jquery";
//Need the context to generate response
var bundleContext = new BundleContext(new HttpContextWrapper(HttpContext.Current), BundleTable.Bundles, bundlePath);
//Bundle class has the method we need to get a BundleResponse
Bundle bundle = BundleTable.Bundles.GetBundleFor(bundlePath);
var bundleResponse = bundle.GenerateBundleResponse(bundleContext);
//BundleResponse has the method we need to call, but its marked as
//internal and therefor is not available for public consumption.
//To bypass this, reflect on it and manually invoke the method
var bundleReflection = bundleResponse.GetType();
var method = bundleReflection.GetMethod("GetContentHashCode", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
//contentHash is whats appended to your url (url?###-###...)
var contentHash = method.Invoke(bundleResponse, null);
The bundlePath variable is the same name that you gave to the bundle (from BundleConfig.cs)
Hope this helps! Good Luck!
Edit: Forgot to say that it would be a good idea to add a test around this. The test would check for the existence of the GetHashCode function. This way, in the future, should the internals of the Bundler change the test will fail and you'll know where the problem is.

Related

Kofax TotalAgility Send a PDF Document to Jobs Queue (KTA)

I'm at a loss with the KTA SDK. My intention is to pass a scanned document in PDF format with a few headers to KTA's jobs queue. As I'm still going through the documentation, my best guess right now is to use the Document class as a DTO then I need to call a method to pass that Document as a parameter:
...
[HttpPost]
public HttpResponseMessage Upload()
{
var httpRequest = System.Web.HttpContext.Current.Request;
var DocType = httpRequest.Headers["X-DocType"];
var Pages = httpRequest.Headers["X-DocPages"];
var Title = httpRequest.Headers["X-DocTitle"];
Agility.Sdk.Model.Capture.Document doc = new Agility.Sdk.Model.Capture.Document();
// doc.DocumentType = DocType; // Type DocumentTypeSummary
doc.NumberOfPages = Convert.ToInt32(Pages);
doc.FileName = Title;
...
I'm just wondering if I'm on the right track in doing this?
My other question is where can we store data from a custom header? In this example, I need to store custom header called Comments and AccountNumber.
Lastly, what service needs to be called to send this document to KTA jobs queue? Would CaptureDocumentService be the right one?
I'd greatly appreciate any help on this.
Start with the details laid out in the Sample App example. It shows what to add to your app.config, but what it doesn’t call out explicitly enough is that you should change the SdkServicesLocation value for your environment. You will simply call the functions in the services within the TotalAgility.Sdk namespaces and it will handle the webservice calls.
The CaptureDocumentService might be part of what you need, and there is a set of samples dedicated to the functions on that service. It refers to the Sample Processes folder, which by default is here:
C:\Program Files\Kofax\TotalAgility\Sample Processes\Capture SDK Sample Package
However what you will definitely need are the functions on the JobService. There are different functions with different options, but CreateJobWithDocuments is probably what you want to start with. You can see that this is creating document(s) and a job together in one step.
There is similarity with the parameters on CaptureDocumentService.CreateDocument3, so you might cross-reference with that to best understand the parameters. The difference is that CreateDocument3 just creates a document in the abstract: you want to actually use it as an input to create a job, so use the combined function.
Finally, to pass fields in, you will be setting RuntimeField objects as part of the RuntimeDocument objects going into your CreateJobWithDocuments call.

SSDT TSqlModel.DeleteObjects method doesn't behave as expected

I've been trying to use the TSqlModel method DeleteObjects to programmatically remove certain users from a Database project. The problem is that when I call the method, the user remains in the model. I wonder if I am calling the method correctly. Here's something close to what I am doing:
modelFromDacpac.DeleteObjects(#"DOMAIN\user");
When I run the following code to see if it's really gone, the user is still there!
var tst_delete= modelFromDacpac.GetObjects(User.TypeClass, new ObjectIdentifier(#"DOMAIN\user"), DacQueryScopes.Default).FirstOrDefault();
tst_delete is non-null and has a name that matches "DOMAIN\user".
Any idea what I'm doing wrong?
Prior to the DeleteObject method call, I insert the following line - where the sqlobj object is a TSqlObject referring to the user I am trying to delete
//For some reason, the logins aren't scripted objects within the DACPAC, and so cannot be deleted using the DeleteObjects method - or maybe they simply cannot be found.
modelFromDacpac.ConvertToScriptedObject(sqlobj, "DOMAIN_user.sql");
Then I call the DeleteObject method as follows:
modelFromDacpac.DeleteObjects("DOMAIN_user.sql");
I'm not sure why this works, but it does. My guess is that the DeleteObject method is pretty picky about how and where it expects to find objects. Or, maybe some objects, like users, are stored in some non-standard fashion which prevents DeleteObjects from finding them. Whatever the reason, but explicitly converting the user to a scripted object with a given name, and passing that given name to the DeleteObjects method, it works.
I am a little concerned that I do not know why it works. The other concern is that it doesn't show up in the official documentation of the TSqlModel object:
https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.dac.model.tsqlmodel_methods(v=sql.120).aspx
But it does work. At least, so far.
DeleteObject caught me out the same way :) - it only deletes scripts added using AddOrUpdate when you also pass in a script name and Delete uses the same script name.
What you need to do is create a new model and add in everything except the things you want to delete.
Why do you want to delete a login? If you don't want it to be deployed you can use a deployment contributor like my one here to exclude the login at deployment time:
https://the.agilesql.club/Blogs/Ed-Elliott/HOWTO-Filter-Dacpac-Deployments
Ed

Can't query/order on built-in rally fields "could not read all instances of class com.f4tech.slm.domain.Artifact"

I'm using v2.0 of the API via the C# dll. But this problem also happens when I pass a Query String to the v2.0 API via https://rally1.rallydev.com/slm/doc/webservice/
I'm querying at the Artifact level because I need both Defects and Stories. I tried to see what kind of query string the Rally front end is using, and it passes custom fields and built-in fields to the artifact query. I am doing the same thing, but am not finding any luck getting it to work.
I need to be able to filter out the released items from my query. Furthermore, I also need to sort by the custom c_ReleaseType field as well as the built-in DragAndDropRank field. I'm guessing this is a problem because those built-in fields are not actually on the Artifact object, but why would the custom fields work? They're not on the Artifact object either. It might just be a problem I'm not able to guess at hidden in the API. If I can query these objects based on custom fields, I would expect the ability would exist to query them by built-in fields as well, even if those fields don't exist on the Ancestor object.
For the sake of the example, I am leaving out a bunch of the setup code... and only leaving in the code that causes the issues.
var request = new Request("Artifact");
request.Order = "DragAndDropRank";
//"Could not read: could not read all instances of class com.f4tech.slm.domain.Artifact"
When I comment the Order by DragAndDropRank line, it works.
var request = new Request("Artifact");
request.Query = (new Query("c_SomeCustomField", Query.Operator.Equals, "somevalue").
And(new Query("Release", Query.Operator.Equals, "null")));
//"Could not read: could not read all instances of class com.f4tech.slm.domain.Artifact"
When I take the Release part out of the query, it works.
var request = new Request("Artifact");
request.Query = (((new Query("TypeDefOid", Query.Operator.Equals, "someID").
And(new Query("c_SomeCustomField", Query.Operator.Equals, "somevalue"))).
And(new Query("DirectChildrenCount", Query.Operator.Equals, "0"))));
//"Could not read: could not read all instances of class com.f4tech.slm.domain.Artifact"
When I take the DirectChildrenCount part out of the query, it works.
Here's an example of the problem demonstrated by an API call.
https://rally1.rallydev.com/slm/webservice/v2.0/artifact?query=(c_KanbanState%20%3D%20%22Backlog%22)&order=DragAndDropRank&start=1&pagesize=20
When I remove the Order by DragAndDropRank querystring, it works.
I think most of your trouble is due to the fact that in order to use the Artifact endpoint you need to specify a types parameter so it knows which artifact sub classes to include.
Simply adding that to your example WSAPI query above causes it to return successfully:
https://rally1.rallydev.com/slm/webservice/v2.0/artifact?query=(c_KanbanState = "Backlog")&order=DragAndDropRank&start=1&pagesize=20&types=hierarchicalrequirement,defect
However I'm not tally sure if the C# API allows you to encode additional custom parameters onto the request...
Your question already contains the answer.
UserStory (HierarchicalRequirement in WS API) and Defect inherit some of their fields from Artifact, e.g. FormattedID, Name, Description, LastUpdateDate, etc. You may use those fields in the context of Artifact type.
The fields that you are trying to access on Artifact object do not exist on it. They exist on a child level, e.g. DragAndDropRank, Release, Iteration. It is not possible to use those fields in the context of Artifact type.
Parent objects don't have access to attributes specific to child object.
Artifact is an abstract type.
If you need to filter by Release, you need to make two separate requests - one for stories, the other for defects.

Updating Code, Find All Objects Of Type

Task:
Rip through all the code in the entire solution and wrap all webservice method-calls in another ws method-call that accepts a GUID (it's a login scenario)
Background :
Hundreds of web services, add token security. As explained to me when I was assigned to the task, we do it this way because if, in the future , some changes to security etc have to be made we can just do it in the WrappermethodClass in stead of having to change hundreds of web services
Tried and failed :
Find all references : too much data , returned more than 1000 hits , most of which are useless as they're only object references.
Rename WS so all references beak, build the project I'm working on and fix as I go : works well with the services not integral to the functionality but as soon as I do it with an important one it's like I shot the Solution through the brain, everything's f****d and and VS just gives up trying.
Current Solution :Open all relevant docs, Find ,select All Open Docs, skip through.
Question : How do I do this as efficiently as possible?
Code (before) :
wsGeneric wsGen = new wsGeneric();
wsGen.DoSomething();
Code (after) :
WrapperMethodClass.DoCheck takes params of (Action, GUID),
wsGeneric wsGen = new wGeneric();
wrapperMethodClass.DoCheck((g) =>
{ wsGen.UserInfo.token = g.ToString();
wsGen.DoSomething();
},Shell.token.Value);
Don´t you have some sort of interface or class where you changed the method signature already?
If you changed your webservice and your Code still compiles i´d say you did something wrong or i don´t understand the question.
Update:
I still don´t get it.
I think you have these options:
Change the method signature (all calls should be broken now, fix all the errors vs gives you and you should be done)
Find all references (of the method, not your webservice-class) and change the calls
If above isn´t possible use "Find in Files" and search for the method-name
If all your webservices inherit from an interface or base class you can refactor this method to add a parameter, all inheriting classes will also have the parameter.
If you pass a login object to each webservice, you can add a GUID element to this object and you're done.
It would be a lot easier if you showed us some code, some function interfaces that you have to change and how.
A better solution may be to just use PostSharp to add the checks to your services. This will solve your business problem (you only need to update your aspects) and is much less error prone then your current approach since you don't have to wory about some new developer forgetting to make the call to DoCheck.
Not having to find all references is a side benefit.

How can I pass a runtime method to a custom attribute or viable alternative approach

Basically I would like to do something like this at the top of my class (I know this doesn't work as it isn't a constant)....
[XmlStorage(IsSingleStorageFile = false, IsSubordinate = true,
StorageLocation = "posts" + GetBlogId()]
Where GetBlogId() would be a static utility method.
I'm building an xml storage framework for a blogging engine I am writing (partly a learning excercise, partly as I want to give something back to the open source) and I thought that the tidiest way to determine the storage location would be to use custom attributes since I would be to use a datacontractserializer anyway.
My only problem at present is determining the location for subordinate type whose location would be determined by the id of their parent. e.g Post < Blog.
My storage path would be something like this...
posts\blogid\postid.xml
Where the blog id would be determined by parsing the url and returning the associated blog. This would allow me to host multiple blogs in one installation whilst keeping post storage files separate to reduce memory overheads when loading posts.
Is this a straight no or is there a better way for me to do what I am attempting?
Edit:
Following John answer I tried this....
private static string GetSubordinatePath(Type type)
{
if (typeof(ISubordinate).IsAssignableFrom(type))
{
object instance = Activator.CreateInstance(type);
return (instance as ISubordinate).ParentGuid.ToString();
}
else
{
// TODO: Localize this.
throw new ArgumentException(
String.Format(
CultureInfo.CurrentCulture,
"The specified type '{0}' does not impliment the ISubordinate interface. Please edit the source appropriately to enable storage.",
type.GetType().Name));
}
}
Which would be called from the class reading the custom attribute.
This works nicely..
It's a straight no for attributes... the values are constants baked into the metadata.
One option you could use would be to have some sort of templating built into whatever uses the attributes... so you could have a storage location of posts\{GetBlogId()} and call the method at execution time. It's not exactly elegant though... you might want to consider using an interface instead.

Categories

Resources