I'm building a self-host application in C# using Service Stack. I'd like the application to share content based on some configuration data.
During AppHost.Configure I'd like to read-in a configuration file and recursively share several directories. The directories may be local or shared folders depending on each element in the configuration.
For example, if my config looks like this:
[
{
"sourceId": "TEST1",
"contentPath": "\\\\nas01\\files"
},
{
"sourceId": "TEST2",
"contentPath": "d:\\files"
}
]
I'd like the directories to be recursively accessible like this:
http://localhost/TEST1/....
http://localhost/TEST2/....
Reading the config file is no problem, really, I just want to know the right way to map these directories so I can use the built-in static handling capabilities of ServiceStack.
So I guess my question is:
What's the right way, in Service Stack, to map a static content directory at run-time?
Many thanks!
-Z
New support for FileSystem Mapping was added in this commit which will now let you register file system mappings by overriding GetVirtualFileSources() in your AppHost, e.g:
public override List<IVirtualPathProvider> GetVirtualFileSources()
{
var existingProviders = base.GetVirtualFileSources();
existingProviders.Add(new FileSystemMapping(this, "TEST1", "\\\\nas01\\files"));
existingProviders.Add(new FileSystemMapping(this, "TEST2", "d:\\files"));
return existingProviders;
}
This change is available from v4.5.5+ that's now available on MyGet.
Related
I am planning to use Playwright .NET to perform automated UI testing in a C# dotnet project. The issue is that I would like to have global configuration that can be set rather than needing to define the same settings repeatedly in the context of each test, but I cannot seem to find any working examples.
The documentation at playwright.dev implies that I should be able to simply include a "playwright.config.js" file at the root of the project, but no clear definition of what the content of that file should be. I have experimented with the example provided for Playwright Node.js, using:
import { PlaywrightTestConfig } from '#playwright/test';
const config: PlaywrightTestConfig = {
use: {
// Browser options
headless: false,
slowMo: 50,
// Context options
viewport: { width: 1280, height: 720 },
ignoreHTTPSErrors: true,
// Artifacts
screenshot: 'only-on-failure',
//video: 'retry-with-video',
},
outputDir: "C:\\stuff\\screenshots",
preserverOutput: 'always',
quiet: false,
};
export default config;
However, these settings do not seem to be applied and there is no indication that the playwright.config.js file is either loading or failing.
Any help or a simple example to get me pointed in the right direction would be much appreciated.
LaunchAsync expects a BrowserTypeLaunchOptions class. You could have that object serialized in a JSON file, parse it and pass that options instance as an argument.
I want to assign Active Directory group name dynamically as an attribute to authorize filter.
Currently we have 2 Active Directory groups, one is for DEV and other is for Prod. However if I have access to dev while debugging in local, I need to have access to the action method. If the AD group is prod, I should not have have access to the action method.
I tried using constants in static class classA
public const string DevActiveDirectoryName = "DevdirectoryName";
public const string ProdActiveDirectoryName = "ProddirectoryName";
My action method is like this:
[Authorize(Roles = ClassA.DevActiveDirectoryName)]
public async task<Iactionresult>GetMemberID()
{
}
The only problem with above solution is if I want to deploy to prod, I need to change code and deploy it. Instead if I can pass value dynamically to attribute that would solve my problem. I have tried many ways. Please suggest the best solution for this case. Is there any workaround for this kind of problem? I appreciate your help.
In order to be able to change the group name for different environments, you need to use configuration settings instead of constants. There are many options on how to provide configuration settings to an ASP.NET Core application. This link gives an overview.
If your application uses the default host builder, it will read configuration settings from a variety of sources, e.g. appsettings.json. You can add a setting to this file (if it does not exist yet, add it to the project), e.g.:
{
"ADGroupName": "ProddirectoryName"
}
For your dev-environment there is a dedicated file appsettings.dev.json that you can use to hold your dev settings:
{
"ADGroupName": "DevdirectoryName"
}
When protecting the controller with an Authorize attribute, you need to provide a constant value to the constructor. As the configuration setting can be changed later, it is (obviously) not constant.
Therefore, you have to set up a policy with a constant name in the ConfigureServices method in Startup.cs:
var adGroupName = Configuration.GetValue<string>("ADGroupName");
services.AddAuthorization(options =>
{
options.AddPolicy("ADGroupPolicy", policy =>
{
// This requirement checks for the group
policy.RequireRole(adGroupName);
});
});
For the controller, you need to add the policy name to the Authorize attribute:
[Authorize("ADGroupPolicy")]
public async task<Iactionresult>GetMemberID()
{
}
You can add an entry in your <appSettings> of your web.Config file and use ConfigurationManager to look up the value that should be assigned to a variable ActiveDirectoryName.
<appSettings>
<add key="ActiveDirectoryName" value="DevdirectoryName" />
... // other keys
</appSettings>
and in your code, you could look up what you have in your web.Config file (Dev for development and Prod for production servers (you dont need to deploy new web.config when deploying new code unless you make changes to it.
public const string ActiveDirectoryName = ConfigurationManager.AppSettings["ActiveDirectoryName"];
If you are using Visual Studio, web.config have two different configs (web.debug.config / web.release.config). You can use debug for development and Release that works on production.
This will stay constant and only your config files are changed,
[Authorize(Roles = ClassA.ActiveDirectoryName)]
public async task<Iactionresult>GetMemberID()
{
}
I have a .NET Core 2.0 project using a JSON configuration file, via the Microsoft.Extensions.Configuration.Json provider.
This application is a utility application to support another application that I do not control.
To keep consistency with that other application's configuration file format/style, multi-word setting keys use dashes between words.
Example JSON:
{
"multi-word-setting": "setting value"
}
Example settings class:
public class AppSettings
{
// Pascal casing, as is typical in C#
public string MultiWordSetting { get; set; }
}
Example application code:
class Program
{
private static void Main ( string[ ] args )
{
IConfigurationBuilder configBuilder = new ConfigurationBuilder( ).SetBasePath( Environment.CurrentDirectory ).AddJsonFile( "my-settings.json", true, true );
IConfigurationRoot configRoot = configBuilder.Build( );
AppSettings config = new AppSettings( );
configRoot.Bind( config );
Console.WriteLine( config.MultiWordSetting );
}
}
However, given that hyphens are illegal in identifiers in C#, how can I follow typical C# naming conventions while also following the defined style of the application I am supporting?
I know I can use Newtonsoft and its JsonPropertyAttribute to just manually deal with the json data, but I'd prefer to make this work without "external" libraries, if possible. Besides, JSON.Net is over 20x larger, and the configuration libraries handle other stuff, such as automatic reloading, binding, merging, and optional files.
I've tried adding DataMember attributes to the properties of my class, to indicate the name of the json property, but that doesn't do the trick.
Is there a way to do this, with the .NET Core Microsoft.Extensions.Configuration.Json provider?
While I was writing the question, I decided to dive into the .net core source and found that no, it is not possible to do what I wanted to do.
So, I decided to fix it and, pending the outcome of ASP.Net Configuration Pull Request 775, it may be possible in a future version of .net core. Please feel free to review/scrutinize that pull request.
Until then, a relatively simple workaround is to bind the configuration as normal and then manually access any unsupported settings via the ConfigurationRoot object's indexer.
Note that, if you're using the automatic reload feature, you'd have to manually handle setting the property on reload, too.
I want to get all informations about files from my github repository using octokit
projectis: http://octokitnet.readthedocs.org/en/latest/contributing/
Updated:
what I thought I can do is
getAllFilesFromRepository
that will return json with something like example below for all files in repository
{
"type": "symlink",
"target": "/path/to/symlink/target",
"size": 23,
"name": "some-symlink",
"path": "bin/some-symlink",
"sha": "452a98979c88e093d682cab404a3ec82babebb48",
"url": "https://api.github.com/repos/octokit/octokit.rb/contents/bin/some-symlink",
"git_url": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/452a98979c88e093d682cab404a3ec82babebb48",
"html_url": "https://github.com/octokit/octokit.rb/blob/master/bin/some-symlink",
"download_url": "https://raw.githubusercontent.com/octokit/octokit.rb/master/bin/some-symlink",
"_links": {
"git": "https://api.github.com/repos/octokit/octokit.rb/git/blobs/452a98979c88e093d682cab404a3ec82babebb48",
"self": "https://api.github.com/repos/octokit/octokit.rb/contents/bin/some-symlink",
"html": "https://github.com/octokit/octokit.rb/blob/master/bin/some-symlink"
}
}
Please note
I do not want to download any files at all or write query with multiple calls to retrieve the data.
I'm not sure I understand the question, but please read the Getting Started guide first around the setup you need.
This is an example of how to download the contents of a given repository:
var github = new GitHubClient(...); // TODO: other setup
var contents = await github
.Repository
.Content
.GetAllContents("octokit", "octokit.net");
...
var docs = await github
.Repository
.Content
.GetAllContents("octokit", "octokit.net", "docs");
Change the values to suit the repository you're interested in. If you want to download a non-default branch, use GetAllContentsByRef instead.
GetAllContents method would work fine but one small issue is that it would not iterate recursively through all the sub-folders in your repository. It gives only the files and folders present in the top-level. If you want to list out all the files of your repository, I would suggest you to use the GetRecursive method as follows:
var trees = _gitHubClient.Git.Tree.GetRecursive(_config.Owner, _config.RepositoryId, <<APPROPRIATE SHA>>).Result;
You can get the SHA for the latest commit or as per your requirement.This method would give you a tree response which has sufficient details such as the SHA, Path, Type and Size.
I'm creating a controller that will serve the combined/minified versions of my JavaScript and CSS. I need to somewhere along the line define which scripts/styles to be loaded.
When a request is made, for example for style.css?VersionNumberHere, it will check if the combined/minified data is already in the HttpContext.Cache, if so spit it out. Otherwise, I need to look up the definition that makes up style.css.
I created a Script/StyleBuilder (that inherits from ContentBuilder) that will store all the paths that need to be combined and then squished (so this would be the definition of style.css).
Where should I be storing these references to the "builders"? Should they be in a static class or a singleton that implements an interface so that it can be tested?
Here's the interface that the abstract class ContentBuilder implements (you can easily imagine the implementation):
public interface IContentBuilder : IEnumerable<string>
{
string Name { get; }
int Count { get; }
string[] ValidExtensions { get; }
void Add(string path);
bool ValidatePath(string path);
string GetHtmlReference(); // Spits out <script>, or <link> depending on implementation.
string Build(); // Minifies, combines etc.
}
And here is ideally what I'd like to be able to do with these:
ContentBuilderContainer.Current.Add("main.js", c => new ScriptBuilder()
{
"/path/to/test.js",
"/path/to/test2.js",
"/path/to/test3.js"
});
ContentBuilderContainer.Current.Add("style.css", c => new StyleBuilder()
{
"/path/to/style.css",
"/path/to/test.less"
});
Then to output all the HTML for all registered IContentBuilder:
ContentBuilder.Container.Current.BuildHtml();
Maybe you should check out SquishIt. Some more info on it in this blog post. We use it in production.
Attach caching attributes to your controller actions and cache by parameter like this:
[OutputCache(Duration = 7200, Location = OutputCacheLocation.Client, VaryByParam = "jsPath;ServerHost")]
[CompressFilter]
// Minifies, compresses JavaScript files and replaces tildas "~" with input serverHost address
// (for correct rewrite of paths inside JS files) and stores the response in client (browser) cache for a day
[ActionName("tildajs")]
public virtual JavaScriptResult ResolveTildasJavaScript(string jsPath, string serverHost)
...
I made the following interface:
public interface IContentBuilderContainer
{
int Count { get; }
bool Add(string name, Func<IContentBuilder> contentBuilder);
string RenderHtml();
}
And then in the implmentation of ContentBuilderContainer:
public class ContentBuilderContainer : IContentBuilderContainer
{
// Other members removed for simplicity.
#region Static Properties
/// <summary>
/// Gets or sets the current content builder container.
/// </summary>
public static IContentBuilderContainer Current
{
get;
set;
}
#endregion
#region Static Constructors
static ContentBuilderContainer()
{
ContentBuilderContainer.Current = new ContentBuilderContainer();
}
#endregion
}
This way there's a single ContentBuilderContainer living at one time.
I helped write some code that did this recently. Here's a high level overview of the solution that was implemented. Hopefully it will give you some good ideas.
Configuration: We created custom configuration elements that define a key and their a corresponding list of directories. So the key JS is linked to our /Content/Scripts folder, and CSS is linked to our /Content/Styles folder. I have seen other solutions where the configuration allowed for individual files to be listed.
Controller: The controller was set up to receive requests something along the lines of /Content/Get/JS and /Content/Get/CSS. The controller uses the configuration key and client request headers to come up with a cache key that identifies the content we want to serve: JS-MSIE-ZIP, CSS-FFX, etc. The controller then checks our cache service. If the content is not there, it gets concatenated, minified, compressed, cached and then served. Handy fallout is that the content is compressed before going into the cache instead of every time it's served.
View: In the View, the links are set up like this:
<link href="<%: Url.Action("Get", "Content", new { key = "CSS" }) %>" rel="stylesheet" type="text/css" />
Cache Service: We're using an existing cache service we have that just wraps the application cache. At some point we'll probably move that to Velocity or something similar. If the amount of CSS and JS we cache keeps growing, we'll probably change the format of the key to a proper filename and move the content to the file system. But, memory's pretty cheap, we'll see what happens.
Reasoning: (if it matters)
We did this in order to keep the JavaScript for different features in separate files in source control without having to link to all of the files individually in the HTML. Because we configure our content by directory and not individual files, we can also run a full minification during production builds to speed up the whole run time process somewhat. Yet we still get the benefit of determining which content to serve based on the client browser, and cached compressed versions.
In development, the system can be set up with a quick configuration change so that every request rebuilds the JS. The files are concatenated with file names injected in comments for easy searching, but the content is not minified and nothing is cached anywhere. This allows us to change, test and debug the JS without recompiling the application.
Couldn't quite find all these features in a package out there so we spent a couple of days and built it. Admittedly some features were just for fun, but that's why we like doing what we do. =)