I'm using the NodeJS OctoKit API and for our business analytics I'm trying to gather a list of all commits across all repositories. So I can make a little "ranking".
const owner = req.params.owner;
const { data } = await octokit.request('GET /user/repos', { type: 'private' })
let total = 0;
data.forEach(element => {
const name = element.name;
octokit.repos.listCommits({
owner,
name,
}).then(r => {
total += r.data.length;
}).catch(error => console.log(error));
})
console.log(total);
I tried something like this but it catches load of 404 errors because I think the repos are private
I guess you solved it by now. How did you initialized octokit?
did you put the correct token ?
const octokit = new Octokit({ auth: <githubToken> });
Related
While not a front-end developer, I'm trying to set up a web app to show up a demo for a product. That app is based on the Sigma.js demo app demo repository.
You'll notice that this app relies on a graph which is hosted locally, which is loaded as:
/src/views/Root.tsx :
useEffect(() => {
fetch(`${process.env.PUBLIC_URL}/dataset.json`)
.then((res) => res.json())
.then((dataset: Dataset) => {...
// do things ....
and I wish to replace this by a call to another service which I also host on Cloud Run.
My first guess was to use the gcloud-auth-library, but I could not make it work - especially since it does not seem to support Webpack > 5 (I might be wrong here), the point here this lib introduces many problems in the app, and I thought I'd be better off trying the other way GCP suggests to handle auth tokens: by calling the Metadata server.
So I replaced the code above with:
Root.tsx :
import { getData } from "../getGraphData";
useEffect(() => {
getData()
.then((res) => res.json())
.then((dataset: Dataset) => {
// do even more things!
getGraphData.js :
import { getToken } from "./tokens";
const graphProviderUrl = '<my graph provider service URL>';
export const getData = async () => {
try {
const token = await getToken();
console.log(
"getGraphData.js :: getData : received token",
token
);
const request = await fetch(
`${graphProviderUrl}`,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
const data = await request.json();
console.log("getGraphData.js :: getData : received graph", data);
return data;
} catch (error) {
console.log("getGraphData.js :: getData : error getting graph data", error);
return error.message;
}
};
tokens.js :
const targetAudience = '<my graph provider service base URL>'; // base URL as audience
const metadataServerAddress = "169.254.169.254"; // use this to shortcut DNS call to metadata.google.internal
export const getToken = async () => {
if (tokenExpired()) {
const token = await getValidTokenFromServer();
sessionStorage.setItem("accessToken", token.accessToken);
sessionStorage.setItem("expirationDate", newExpirationDate());
return token.accessToken;
} else {
console.log("tokens.js 11 | token not expired");
return sessionStorage.getItem("accessToken");
}
};
const newExpirationDate = () => {
var expiration = new Date();
expiration.setHours(expiration.getHours() + 1);
return expiration;
};
const tokenExpired = () => {
const now = Date.now();
const expirationDate = sessionStorage.getItem("expirationDate");
const expDate = new Date(expirationDate);
if (now > expDate.getTime()) {
return true; // token expired
}
return false; // valid token
};
const getValidTokenFromServer = async () => {
// get new token from server
try {
const request = await fetch(`http://${metadataServerAddress}/computeMetadata/v1/instance/service-accounts/default/token?audience=${targetAudience}`, {
headers: {
'Metadata-Flavor': 'Google'
}
});
const token = await request.json();
return token;
} catch (error) {
throw new Error("Issue getting new token", error.message);
}
};
I know that this kind of call will need to be done server-side. What I don't know is how to have it happen on a React + Node app. I've tried my best to integrate good practices but most questions related to this topic (request credentials through a HTTP (not HTTPS!) API call) end with answers that just say "you need to do this server-side", without providing more insight into the implementation.
There is a question with similar formulation and setting here but the single answer, no upvote and comments is a bit underwhelming. If the actual answer to the question is "you cannot ever call the metadata server from a react app and need to set up a third-party service to do so (e.g. firebase)", I'd be keen on having it said explicitly!
Please assume I have only a very superficial understanding of node.js and React!
I am trying to get all of the posts that are listed under a group (essentially the topic of a post). Each post document has a field called group_id, which contains the id of the groups document. When I try to search for the posts that contain a specific group_id (given via a link parameter) using the following code, it returns every post in the db, even though they are not listed under the group.
Backend:
const getGroupPosts = async (req, res) => {
const group_id = req.params.id
const posts = await Post.find({group_id}).sort({createdAt: -1})
res.status(200).json(posts)
}
Frontend:
useEffect(() => {
const fetchPosts = async () => {
const response = await fetch(`/api/posts/group/${params.groupId}`, {
headers: {'Authorization': `Bearer ${user.token}`},
})
const json = await response.json()
if (response.ok) {
dispatch({type: 'SET_POSTS', payload: json})
}
}
if (user) {
fetchPosts()
}
}, [dispatch, user])
I am fairly certain that this is an issue with my backend code as I have the same issue when I use postman, although maybe I need to declare another header(?). All help appreciated!
I try to create some API to external adobe stock.
Like in the title, first time i get query from Link router of undefined, but after reload page it work correctly. My
main page
<Link
href={{
pathname: "/kategoria-zdjec",
query: images.zdjecia_kategoria
}}
as={`/kategoria-zdjec?temat=${images.zdjecia_kategoria}`}
className={classes.button}>
</Link>
and my server
app
.prepare()
.then(() => {
server.get("/kategoria-zdjec", async (req, res) => {
const temat = await req.query.temat;
console.log(temat)
const url = `https://stock.adobe.io/Rest/Media/1/Search/Files?locale=pl_PL&search_parameters[words]=${temat}&search_parameters[limit]=24&search_parameters[offset]=1`;
try {
const fetchData = await fetch(url, {
headers: { ... }
});
const objectAdobeStock = await fetchData.json();
res.json(objectAdobeStock);
const totalObj = await objectAdobeStock.nb_results;
const adobeImages = await objectAdobeStock.files;
} catch (error) {
console.log(error);
}
});
and that looks like getInitialProps on page next page
Zdjecia.getInitialProps = async ({req}) => {
const res = await fetch("/kategoria-zdjec");
const json = await res.json();
return { total: json.nb_results, images: json.files };
}
I think it is problem due asynchronous.
I think this might be due to the fact that you are using fetch which is actually part of the Web API and this action fails when executed on server.
You could either use isomorphic-fetch which keeps fetch API consistent between client and server, or use node-fetch when fetch is called on the server:
Zdjecia.getInitialProps = async ({ req, isServer }) => {
const fetch = isServer ? require('node-fetch') : window.fetch;
const res = await fetch("/kategoria-zdjec");
const json = await res.json();
return { total: json.nb_results, images: json.files };
}
This problem is solved, the issue was in another part of my app, directly in state management, just created new variables, and pass to link state value.
I'm in the process of designing a chat bot and trying to find some Node.js sample code and/or documentation on how to implement the Azure Maps service as part of Bot Framework V4. There are many examples of how this is accomplished in V3, but there seems to be no examples of a V4 solution for Node.js. I'm looking to create a step in my botbuilder-dialog flow that would launch a simple "where do we ship it too" location dialog that would guide the user through the dialog and store the address results as part of that users profile. Any help or advice on this would be appreciated.
Yes, this is doable. I created a class (probably overkill, but oh well) in which I make my API call, with my supplied parameters, to get the map. I decided to use Azure Maps (vs Bing Maps) only because I was curious in how it differed. There isn't any reason you couldn't do this with Bing Maps, as well.
In the bot, I am using a component dialog because of how I have the rest of my bot designed. When the dialog ends, it will fall off the stack and return to the parent dialog.
In my scenario, the bot presents the user with a couple choices. "Send me a map" generates a map and sends it in an activity to the client/user. Anything else sends the user onward ending the dialog.
You will need to decide how you are getting the user's location. I developed this with Web Chat in mind, so I am getting the geolocation from the browser and returning it to the bot to be used when getMap() is called.
const { ActivityTypes, InputHints } = require('botbuilder');
const fetch = require('node-fetch');
class MapHelper {
async getMap(context, latitude, longitude) {
var requestOptions = {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow'
};
const result = await fetch(`https://atlas.microsoft.com/map/static/png?subscription-key=${ process.env.AZURE_MAPS_KEY }&api-version=1.0&layer=basic&zoom=13¢er=${ longitude },${ latitude }&language=en-US&pins=default|al.67|la12 3|lc000000||'You!'${ longitude } ${ latitude }&format=png`, requestOptions)
.then(response => response.arrayBuffer())
.then(async result => {
const bufferedData = Buffer.from(result, 'binary');
const base64 = bufferedData.toString('base64');
const reply = { type: ActivityTypes.Message };
const attachment = {
contentType: 'image/png',
contentUrl: `data:image/png;base64,${ base64 }`
};
reply.attachments = [attachment];
await context.sendActivity(reply, null, InputHints.IgnoringInput);
})
.catch(error => {
if (error) throw new Error(error);
});
return result;
};
};
module.exports.MapHelper = MapHelper;
const { ChoicePrompt, ChoiceFactory, ComponentDialog, ListStyle, WaterfallDialog } = require('botbuilder-dialogs');
const { MapHelper } = require('./mapHelper');
const CONFIRM_LOCALE_DIALOG = 'confirmLocaleDialog';
const CHOICE_PROMPT = 'confirmPrompt';
class ConfirmLocaleDialog extends ComponentDialog {
constructor() {
super(CONFIRM_LOCALE_DIALOG);
this.addDialog(new ChoicePrompt(CHOICE_PROMPT))
.addDialog(new WaterfallDialog(CONFIRM_LOCALE_DIALOG, [
this.askLocationStep.bind(this),
this.getMapStep.bind(this)
]));
this.initialDialogId = CONFIRM_LOCALE_DIALOG;
}
async askLocationStep(stepContext) {
const choices = ['Send me a map', "I'll have none of this nonsense!"];
return await stepContext.prompt(CHOICE_PROMPT, {
prompt: 'Good sir, may I pinpoint you on a map?',
choices: ChoiceFactory.toChoices(choices),
style: ListStyle.suggestedAction
});
}
async getMapStep(stepContext) {
const { context, context: { activity } } = stepContext;
const text = activity.text.toLowerCase();
if (text === 'send me a map') {
const { latitude, longitude } = activity.channelData;
const mapHelper = new MapHelper();
await mapHelper.getMap(context, latitude, longitude);
const message = 'Thanks for sharing!';
await stepContext.context.sendActivity(message);
return await stepContext.endDialog();
} else {
await stepContext.context.sendActivity('No map for you!');
return await stepContext.endDialog();
}
}
}
module.exports.ConfirmLocaleDialog = ConfirmLocaleDialog;
module.exports.CONFIRM_LOCALE_DIALOG = CONFIRM_LOCALE_DIALOG;
Hope of help!
---- EDIT ----
Per request, location data can be obtained from the browser using the below method. It is, of course, dependent on the user granting access to location data.
navigator.geolocation.getCurrentPosition( async (position) => {
const { latitude, longitude } = position.coords;
// Do something with the data;
console.log(latitude, longitude)
})
I would like to create a dashboard with graphs about costs of my azure resources (as detailed as possible). Meaning, a list of monthly invoices is not enough (but I would already be very happy if I a could achieve that!!)
Anyway, the first thing I noticed is that if you find an example the endpoint urls look like this
https://management.azure.com/subscriptions/${subscriptionId}/resourcegroups?api-version=2016-09-01
Check the end of the url 2016-09-01, doesn't look very up2date. This medium post was the best article I could find, but it also uses these urls.
Furthermore, I was not able to follow the steps described, first it uses postman to retrieve an access_token (not very useful for me because I need it automated) and second, somewhere in the middle an access_token is retrieved but never used.
So, I found a npm packages like [azure-arm-billing][2] from which I was able to write the following program (mostly copy-paste):
const msRestAzure = require('ms-rest-azure');
const BillingManagement = require('azure-arm-billing')
const clientId = process.env['CLIENT_ID'];
const secret = process.env['APPLICATION_SECRET'];
const domain = process.env['DOMAIN'];
const subscriptionId = process.env['AZURE_SUBSCRIPTION_ID'];
// Retrieve access_token
const app = new msRestAzure.ApplicationTokenCredentials(clientId, domain, secret);
app.getToken((err, token) => {
console.log(token.accessToken);
});
// =======
msRestAzure
.interactiveLogin( { domain }) // The argument here is nowhere documented
.then(credentials => {
console.log(credentials);
let client = new BillingManagement(credentials, subscriptionId);
return client.invoices.list();
})
.then(invoices => {
console.log('List of invoices:');
console.dir(invoices, { depth: null, colors: true });
});
Running this shows a nice access_token and invoices
...
List of invoices:
[
{
id: '/subscriptions/../providers/Microsoft.Billing/invoices/....',
name: '...',
type: 'Microsoft.Billing/invoices',
invoicePeriodStartDate: 2019-08-25T00:00:00.000Z,
invoicePeriodEndDate: 2019-09-24T00:00:00.000Z,
billingPeriodIds: [
'/subscriptions/.../pr..s/Micro..ing/bill..ods/201910-1'
]
},
{
id: '/subscriptions/9ea...3d/providers/Microsoft.Billing/invoices/201909-...',
name: '....',
type: 'Microsoft.Billing/invoices',
invoicePeriodStartDate: 2019-07-25T00:00:00.000Z,
invoicePeriodEndDate: 2019-08-24T00:00:00.000Z,
billingPeriodIds: [
'/subscriptions/..../providers/Microsoft.Billing/billingPeriods/201909-1...'
]
}
]
Although I have my invoices, there are no numbers. And I would like to retrieve costs for every resources.
So the documentation seems to be outdated up to not existing for what I want (as it seems). My question is if someone was able to retrieve information like this? I would really like to know how!!
UPDATE
It seems to be a permission issue. So, below I share some screenshots showing what I have right now. Maybe from these it is clear what I miss or have setup incorrectly. So first, here is my latest nodejs app:
const msRestAzure = require("ms-rest-azure");
const ConsumptionManagementClient = require("azure-arm-consumption");
const clientId = '76d79....'; // App registration ID
const secret = '****...'; // App registration secret
const domain = 'dc36...'; // tenantId
const subscriptionId = '9ea2d...'; // subscription ID
const AzureServiceClient = msRestAzure.AzureServiceClient;
//an example to list resource groups in a subscription
msRestAzure.loginWithServicePrincipalSecret(clientId, secret, domain).then((creds) => {
const client = new ConsumptionManagementClient(creds, subscriptionId);
const expand = '';
const filter = '';
const skiptoken = '';
const top = 1000;
const apply = '';
return client.usageDetails.list(expand, filter, skiptoken, top, apply).then(result => {
console.log('The result is:', result);
});
}).catch((err) => {
console.log('An error occurred:');
console.dir(err, { depth: null, colors: true });
});
Which outputs a statusCode 401
Error: Unauthorized. Request ID: e6b127...
...
So, I have in AD an App registration
Its API permissions are
Finally, I have just one subscription
With the following IAM settings
Any suspicious?
If you're looking for resource costs, I would suggest that you take a look at Consumption API - List Usage Details. That will give you the consumption for all the resources.
You will need to install azure-arm-consumption package.
Here's the sample code:
const msRestAzure = require("ms-rest-azure");
const ConsumptionManagementClient = require("azure-arm-consumption");
msRestAzure.interactiveLogin().then((creds) => {
const subscriptionId = "<your subscription id>";
const client = new ConsumptionManagementClient(creds, subscriptionId);
const expand = "";
const filter = "";
const skiptoken = "";
const top = 1000;
const apply = "";
return client.usageDetails.list(expand, filter, skiptoken, top, apply).then((result) => {
console.log("The result is:");
console.log(result);
});
}).catch((err) => {
console.log('An error occurred:');
console.dir(err, {depth: null, colors: true});
});
This is taken from here: https://github.com/Azure/azure-sdk-for-node/tree/master/lib/services/consumptionManagement.