How to use list of pages from graph.facebook.com/me/accounts - facebook

https://graph.facebook.com/me/accounts?access_token=USERS_AUTH_TOKEN
returns a list of pages the user has admin status in (in JSON format).
I would like to list all the pages in a dropdownlist, and make the user choose which facebook page he wants to use (on my webapp), so I can obtain the specific access token for that facebook page.
My question is - whats the easiest and best way to do that. Ive never worked with JSON before, but I guess theres a pretty easy was to do this through the facebook-sdk.

Since you're using the C# SDK, just take the array of objects and convert them into a IList<IDictionary>() array using the pageId as the key and the value being the page name.
This is not fully compilable, but you get the idea:
private void IList<IDictionary<long,string>> ConvertToList(dynamic meAccounts)
{
foreach(var acc in meAccounts.data)
{
yield return new Dictionary((long)acc.id, (string)acc.name);
}
{

Okay figured out a way to do it. But I have no idea ifs the right way or the most optimal.
Would very much like inputs on it.
[DataContract]
internal class FacebookObj
{
[DataMember]
public List<FacebookAccount> data;
[DataMember]
public FacebookNext paging;
}
[DataContract]
internal class FacebookAccount
{
[DataMember]
public string name;
[DataMember]
public string category;
[DataMember]
public string id;
[DataMember]
public string access_token;
}
[DataContract]
internal class FacebookNext
{
[DataMember]
public string next;
}
public void ShowPages(string authToken) {
WebRequest webRequest = WebRequest.Create("https://graph.facebook.com/me/accounts?access_token=" + authToken);
WebResponse webResponse = webRequest.GetResponse();
Stream sr = webResponse.GetResponseStream();
if (sr != null)
{
jsonSer = new DataContractJsonSerializer(typeof(FacebookObj));
FacebookObj o = (FacebookObj)jsonSer.ReadObject(sr2);
foreach (FacebookAccount s in o.data)
{
//Do stuff
Response.Write(s.id + " - " + s.name + "<br />");
}
}
}

Related

Blazor 6.0 await HttpClient.GetFromJsonAsync<List CS8601 Possible null reference asignment

Hello Everyone im missing something fairly fundamental here and i could really appreciate some guidance .
I am fairly new to Blazor and the entity framework and am building one of my first Blazor apps
I have a fairly basic data class
using System.ComponentModel.DataAnnotations;
namespace BIDM2.Data
{
public class InvalidAddressMapping
{
[Key]
public int record_no { get; set; } = 0;
public string sales_org_pos { get; set; } = " ";
public string press_sales_mgr_code { get; set; } = " ";
public string press_sales_mgr_Name { get; set; } = " ";
public string forming_sales_rep_code { get; set; } = " ";
public string forming_sales_rep_Name { get; set; } = " ";
}
}
that i am using in my controller as follows
[HttpGet]
public async Task <ActionResult<List<InvalidAddressMapping>>> GetInvalidAdressMappings()
{
return await _db.GetInvalidAddressMappings.FromSqlRaw("EXEC BIDM_GetInvalidAddressMappings;").ToListAsync();
}
and im trying to use it in my razor page like this
#code {
List<InvalidAddressMapping> invalidMappings = new List<InvalidAddressMapping>();
protected override async Task OnInitializedAsync()
{
invalidMappings = await HttpClient.GetFromJsonAsync<List<InvalidAddressMapping>>(NavigationManager.BaseUri + "invalidmapping");
}
}
how ever when i try and run my razor page im getting an error CS8601 Possible null Reference asignment :(
Im not sure i totaly understand and i could use some guidance to point me in the right direction it has to be somethign fairly fundamental i am missing
interestingly this all came about because im trying to convert an existing non async method to an async method
when i use
//1. get invalid address mappings
public InvalidAddressMapping[] GetInvalidAddressMappings()
{
InvalidAddressMapping[] invMappings;
// exec BIDM_GetInvalidAddressMappings calls the stored procedure
invMappings = _dbcontext.GetInvalidAddressMappings.FromSqlRaw("EXEC BIDM_GetInvalidAddressMappings;").ToArray();
return invMappings;
}
}
it works beautifully and i can see a lovely list of JSON data
please help a struggling old git out and pint me in a direction where i can understand where i am going wrong :)
thank you every one
The GetFromJsonAsync extension method returns a nullable type. In your case, it is List<InvalidAddressMapping>? (note the extra ? on the end there). If it fails to deserialise properly, for example, it could return null. The code is telling you that you need to check for a null response to be safe.
So the safe version is to do something like this:
var result = await HttpClient.GetFromJsonAsync<List<InvalidAddressMapping>>(
NavigationManager.BaseUri + "invalidmapping");
if(result == null)
{
// Do something here to handle this error, for example:
throw new Exception("Can't get data from the server for some reason, help!");
}
invalidMappings = result;
Also note that the CS8601 message you see is a warning, not an error. So technically you could ignore it, though I would strongly advise against doing that.

ASP MVC EF6 Multi Tenant based on host

Sorry, another multi tenancy post. I can't find a good solution to site, I have read tons of great posts on multi tenancy for ASP MVC but I still need some good advice.
I have an ASP MVC Entity Framework 6 Code First web application. This app has to work for many different clients using a single database for all of them.
I have an entity for all the clients, and each client can have different hosts.
public class Client
{
public int ClientId { get; set; }
public string Name { get; set; }
...
public ICollection<ClientHost> Hosts { get; set; }
}
public class ClientHost
{
public int ClientId { get; set; }
public Client Client { get; set; }
public string Name { get; set; }
}
I have added a column "ClientId" to all the entities I need to filter, so I can separate data from different clients.
public class SomeEntity
{
public int Id { get; set; }
...
public int ClientId { get; set; }
}
First thing I need is, base on the host, retrieve the ClientId to work with.
private static int GetClientId()
{
var currentClient = Convert.ToInt32(HttpRuntime.Cache[CacheClient]);
if (currentClient != null) return currentClient;
lock (Synclock)
{
using (var dataContext = new MyDataContext())
{
var urlHost = HttpContext.Current.Request.Url.Host;
currentClient = dataContext.Clients
.FirstOrDefault(p => p.Hosts.Any(h => h.Name == urlHost));
if (currentClient == null) return null;
HttpRuntime.Cache.Insert(CacheClient, currentClient, null, Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(0), CacheItemPriority.Default, null);
return currentClient;
}
}
}
QUESTION 1
As you see I get the clientId from DB and store it in cache, so I don't have to call DB every time I need it.
I don't know if there is a better approach to get the client Id or, better, to store it.
EDIT
After investigation I have created a variable in DbCOntext and initialize it in the Startup.cs file.
public class MyDataContext : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public static string ClientId { get; set; }
public MyDataContext() : base("MyDataBase") { }
public static MyDataContext Create()
{
return new myDataContext();
}
....
}
In Startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
MyDataContext.ClientId = ClientConfiguration.GetCurrentClientId();
ConfigureAuth(app);
}
}
QUESTION 2
Once I have the ClientId, I need to add a filter to every query that needs it. Doing this manually can take you to make many errors or forget to do it in some places.
I need a way that the application can add the filter to all queries automatically (only those entities that need it), so I don't have to worry about a client getting other client's data. Also I need to add the ClientId to all the Insert and Update commands.
I have read about filtering and/or use EF Interceptors, but after reading some posts about that I can't figure out how to do it. Need some help here.
Thanks in advance.
EDIT
In order to solve QUESTION 2 I have followed this great post by Xabikos:
http://xabikos.com/2014/11/17/Create-a-multitenant-application-with-Entity-Framework-Code-First-Part-1/
I have changed it a little bit, since I don't use Users to get the current tenant and instead I use the host. This is part of the program I don't know yet how I'm going to solve but, assuming I already have the ClientId I can add filters to all the queries without realizing that is happening:
I have replaced all the user logic:
private static void SetTenantParameterValue(DbCommand command)
{
if (MyDataContext.ClientId == 0) return;
foreach (DbParameter param in command.Parameters)
{
if (param.ParameterName != TenantAwareAttribute.TenantIdFilterParameterName)
continue;
param.Value = MyDataContext.ClientId;
}
}
Same in all the places...
Than I only have to mark the entities that have to filter with TenantAware, indicating the property. In this case I do in my base class and then apply that base class to all the entities I need.
[TenantAware("ClientId")]
public abstract class ClientEntity : Entity, IClientEntity
{
public int ClientId { get; set; }
public Client Client { get; set; }
}
Here are a couple of things I have done in the past that might help.
Question 1:
I am not a big fan of session as the web is supposed to be stateless. However, it is sometimes necessary. Your approach is reasonable. You could also use cookies as well. What I use are Json Web Tokens (JWT) via my authentication provider (Auth0.com). For each request as it is authenticated, I look for this client id. Here is an example. This is MVC 6 as well. You could do the same type of things w/ cookies.
public class Auth0ClaimsTransformer : IClaimsTransformer
{
private string _accountId = AdminClaimType.AccountId.DefaultValue;
private string _clientId = AdminClaimType.ClientId.DefaultValue;
private string _isActive = AdminClaimType.IsActive.DefaultValue;
public Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
{
foreach (var claim in context.Principal.Claims)
{
switch (claim.Type)
{
case "accountId":
_accountId = claim.Value ?? _accountId;
break;
case "clientId":
_clientId = claim.Value ?? _clientId;
break;
case "isActive":
_isActive = claim.Value ?? _isActive;
break;
}
}
((ClaimsIdentity)context.Principal.Identity)
.AddClaims(new Claim[]
{
new Claim(AdminClaimType.AccountId.DisplayName, _accountId),
new Claim(AdminClaimType.ClientId.DisplayName, _clientId),
new Claim(AdminClaimType.IsActive.DisplayName, _isActive)
});
return Task.FromResult(context.Principal);
}
Then in my Startup.cs Configure method I plug in my claims transformer.
app.UseJwtBearerAuthentication(options);
app.UseClaimsTransformation(new ClaimsTransformationOptions
{
Transformer = new Auth0ClaimsTransformer()
});
Next I use a base authentication controller that parses out my claims into properties I can use in my controller.
[Authorize]
[Route("api/admin/[controller]")]
public class BaseAdminController : Controller
{
private long _accountId;
private long _clientId;
private bool _isActive;
protected long AccountId
{
get
{
var claim = GetClaim(AdminClaimType.AccountId);
if (claim == null)
return 0;
long.TryParse(claim.Value, out _accountId);
return _accountId;
}
}
public long ClientId
{
get
{
var claim = GetClaim(AdminClaimType.ClientId);
if (claim == null)
return 0;
long.TryParse(claim.Value, out _clientId);
return _clientId;
}
}
public bool IsActive
{
get
{
var claim = GetClaim(AdminClaimType.IsActive);
if (claim == null)
return false;
bool.TryParse(claim.Value, out _isActive);
return _isActive;
}
}
public string Auth0UserId
{
get
{
var claim = User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
return claim == null ? string.Empty : claim.Value;
}
}
private Claim GetClaim(AdminClaimType claim)
{
return User.Claims.FirstOrDefault(x => x.Type == claim.DisplayName);
}
Finally in my controller it is trivial to extract which tenant is making the call. e.g.
public FooController : BaseController
{
public async Task<IActionResult> Get(int id)
{
var foo = await _fooService.GetMultiTenantFoo(ClientId, id);
return Ok(foo);
}
}
Question 2:
One of the ways I have used in the past is create a BaseMultiTenant class.
public class BaseMultiTenant
{
public int ClientId {get;set;}
public virtual Client Client {get;set;}//if you are using EF
}
public class ClientHost : BaseMultiTenant
{
public string Name {get;set;}
//etc
}
Then simply create an extension method for multi-tenant based entities. I know this doesn't "do it automatically" but it is an easy way to ensure each multi-tenant entity is being called only by its owner.
public static IQueryable<T> WhereMultiTenant<T>(this IQueryable<T> entity, int clientId, Expression<Func<T, bool>> predicate)
where T : BaseMultiTenant
{
return entity.Where(x => x.ClientId == clientId)
.Where(predicate);
}
Then when someone calls for their resource you can:
var clientHost = _myContext.ClientHosts
.WhereMultiTenant(ClientId,
x => x.Name == "foo")
.FirstOrDefault();
Hope this is helpful.
Also found a similar example using an interface.

Web API Model binding - Complex Parameter

I have a string received in query string
f[0][p]=zap&f[0][v][] = 110&f[0][v][]=500&f[1][p] = prih&f[1][v][] = 10000000&f[1][v][] = 30000000000
I try to catch it with string[] but it is always null. How to represent this parameter to Web API so it can serialize it?
I have a workaround with reading it for Uri as a string and then parse it but it is not good for unit testing and would like to update it.
Is it even possible with structure like this?
Ok, I solved this, maybe it can help someone.
I needed a class to represent the received object :
public class Filter
{
private string p { get; set; }
private string[] v { get; set; }
private string cp { get; set; }
private string[] cv { get; set; }
}
And in method definition I needed:
public SubjectStats BusinessSubjects(string country, string start, string end, [FromUri] Filter[] f)
The key was to define object as Filter[] in method signature.

Structuring REST URI's with Jersey

New to Jersey(REST Framework for Java) and I'm trying to setup two resources, in two separate classes that share a root path, and I'm having a problem.
So, I have something like:
#Path("/users")
public class User extends RestSupport {
#GET
#Path("/{user_uuid}")
public String get(#PathParam("user_uuid") String uuid) {
return "Hello User " + uuid;
}
}
The above class works. However, I want to create a child resource in a separate class. But when I do this, it seems to create a URI naming conflict. So, here, I want to get all the pets for a particular users
#Path("/users")
public class Pets extends RestSupport {
#GET
#Path("/{user_uuid}/pets")
public String get(#PathParam("user_uuid") String uuid) {
return "Hello Pets " + uuid;
}
}
These top-level resources have lots of child resources, so I'm looking for the best way to organize them. Any help would be appreciated.
Change the path of Pets class from #Path("/users")to#Path("/users/{user_uuid}/pets")
Don't add the HTTP annotation #GET on your Users root resource method if you want Jersey to delegate calls to a child resource. Consider a User class:
public class User {
String uuid;
User(String id) { this.uuid = id; }
#GET
public String get() { return "Hello user " + uuid; }
#GET
#Path("/pets")
public String getPets() { return "Hello pets " + uuid; }
}
and then adjust your Users resource:
#Path("/users")
public class Users {
#Path("/{user_uuid}")
public User get(#PathParam("user_uuid") String uuid) {
// Get the user from the DAO here...
return new User(uuid);
}
}

How to edit value of ValueProxy of gwt requestfactory at client side?

I have 2 models: ContactGroup and Contact. ContactGroup contains many Contacts.
In the page, I have to display a list of groups and number of contacts in the correspondence group like this:
Group Foo (12 contacts)
Group Bar (20 contacts)
So I at server side I used a DTO ContactGroupInfo:
public class ContactGroupInfo {
private Integer contactCount;
private Long id;
private String name;
public Integer getContactCount() { return this.contactCount; }
public Long getId() { return this.id; }
public String getName() { return this.name; }
public void setContactCount(Integer count) { this.contactCount = count; }
public void setId(Long id) { this.id = id; }
public void setName(String name) { this.name = name; }
}
In this ContactGroupInfo, I added contactCount field which is not a field in ContactGroup entity.
And at client side, I used a ValueProxy:
#ProxyFor(value = ContactGroupInfo.class, locator = ContactGroupService.class)
public interface LightContactGroupProxy extends ValueProxy {
Integer getContactCount();
Long getId();
String getName();
void setContactCount(Integer count);
void setId(Long id);
void setName(String name);
}
So when server side returns to client side a list of LightContactGroupProxy, I stored that list a in ArrayList to render to a CellTable.
And here is the problem comes to me: when I need to edit the name of the group at client side, I can't edit the LightContactGroupProxy object directly.
So I have to send the new name to server to return a new LightContactGroupProxy with the new name. This is not effective because I have to count contacts again (althought I know the number of contacts does not change).
Or I have to send both the number of contacts and new name to server to create a new LightContactGroupProxy with the new name. This is not I want, because if LightContactGroupProxy has many other fields I have to send many fields.
I don't know why GWT teams designs the immutable proxy. So please, someone has experience on requestfactory please show me the correct way to handle ValueProxy returned from server so that we can use them to render and edit?
Thank you
Maybe you should try something like this :
ContactGroupContext ctx = requestFactory.newContactGroupContext();
LightContactGroupProxy editableProxy = ctx.edit(lightContactGroupProxy);
editableProxy.setName(newName);
ctx.saveInfoAndReturn(editableProxy).fire(receiver); // or just ctx.fire();
Anyway, I wouldn't use ValueProxy in this case, I would directly get the ContactGroup entities with a transiant property contactCount. The property could be a primitive, or a ValueProxy if you don't want it to be calculated every time a ContactGroup is requested.