MS Teams Invalid tab settings provided: url while saving the customs configurableTabs - tabs

I got this above error while adding configurableTabs into teams app."Invalid tab settings provided: url". I tried to to add domain into validDomains. but still i got the same error.
I tried different ways and google it but id did not get the answer.
Manifest
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.7/MicrosoftTeams.schema.json",
"manifestVersion": "1.7",
"version": "1.2.1",
"id": "54dg3445-34d4-dfg456-sdfsdfsd-sadfsdf",
"packageName": "com.test.app",
"developer": {
"name": "test",
"websiteUrl": "https://test.se",
"privacyUrl": "https://test.se/test1/gdpr/",
"termsOfUseUrl": "https://test.se"
},
"icons": {
"color": "color.png",
"outline": "outline.png"
},
"name": {
"short": "Test Name",
"full": "Test Name"
},
"description": {
"short": "Handle your",
"full": "Test NameTest NameTest NameTest NameTest NameTest NameTest NameTest NameTest NameTest Name."
},
"accentColor": "#EA5B0C",
"configurableTabs": [
{
"configurationUrl": "https://test.se/check/config",
"canUpdateConfiguration": true,
"scopes": [
"team"
]
}
],
"permissions": [
"identity",
"messageTeamMembers"
],
"validDomains": [
"test.se",
]
}
config
app.getContext().then(async (context) => {
await (window as any).microsoftTeams.app.initialize();
saveQueue = (id: any) => {
(window as any).microsoftTeams.pages.config.registerOnSaveHandler((saveEvent: any) => {
const configPromise = (window as any).microsoftTeams.pages.config.setConfig({
websiteUrl: "https://test.se",
contentUrl: `https://test.se/collaborative/${id}`,
entityId: "grayIconTab",
suggestedDisplayName: "test"
});
configPromise.
then((result: any) => {saveEvent.notifySuccess()}).
catch((error: any) => {saveEvent.notifyFailure("failure message")});
});
}
})

Related

Sort values of Observable array of objects

My service get data from Api :
async getCommunes(){
return await this._http.get<any[]>(this.api)
.pipe()
.toPromise()
.then((response: any) => {
this._communes$.next(response);
})
.catch(err => console.log(err))
}
I call service in a component :
public communes$: Observable<any>;
constructor(private apiService: ApiService) { }
ngOnInit(): void {
this.loadCommunes();
}
get communesFromService$(){
return this.apiService.communes$;
}
async loadCommunes(){
await this.apiService.getCommunes();
this.communes$ = this.apiService.communes$;
}
I display data in Html by calling communesFrom Service$ (get method) :
<div *ngFor="let record of (communes$ | async)?.records">{{record?.fields?.name}}</div>
My data are formated like below :
{
"nhits": 38,
"parameters": {
"dataset": "communes-de-la-province-de-namur",
"rows": 3,
"start": 0,
"format": "json",
"timezone": "UTC"
},
"records": [
{
"datasetid": "communes-de-la-province-de-namur",
"recordid": "5f50799324d15038c9708e48c6a32c907922d00a",
"fields": {
"nsi": "93088",
"geo_shape": {
"coordinates": [
[
[
4.516510705656462,
50.31531488802985
]
]
],
"type": "Polygon"
},
"geo_point_2d": [
50.26709679846407,
4.433557385138468
],
"name": "Walcourt"
},
"geometry": {
"type": "Point",
"coordinates": [
4.433557385138468,
50.26709679846407
]
},
"record_timestamp": "2019-02-13T15:00:55.334000+00:00"
},
{
"datasetid": "communes-de-la-province-de-namur",
"recordid": "11fc2dbd6852c06f095e71adee7e0c1d334ccf24",
"fields": {
"nsi": "91054",
"geo_shape": {
"coordinates": [
[
[
4.971517147861946,
50.02965137853804
]
]
],
"type": "Polygon"
},
"geo_point_2d": [
49.98493619689069,
4.901523112944346
],
"name": "Gedinne"
},
"geometry": {
"type": "Point",
"coordinates": [
4.901523112944346,
49.98493619689069
]
},
"record_timestamp": "2019-02-13T15:00:55.334000+00:00"
},
{
"datasetid": "communes-de-la-province-de-namur",
"recordid": "82a588f6f7d3250b78564fd7a1f16c0a02d0e30a",
"fields": {
"nsi": "92045",
"geo_shape": {
"coordinates": [
[
[
4.827793680052161,
50.41154443216362
]
]
],
"type": "Polygon"
},
"geo_point_2d": [
50.435523543496686,
4.754395027090274
],
"name": "Floreffe"
},
"geometry": {
"type": "Point",
"coordinates": [
4.754395027090274,
50.435523543496686
]
},
"record_timestamp": "2019-02-13T15:00:55.334000+00:00"
}
]
}
I only need 'name' field but order by ascending ('records' array).
I have tried this but it doesn't work. I also tried other solution but without success.:
get communesFromService$(){
return this.apiService.communes$.pipe(
map((response => response.sort((a,b) => a.name - b.name)))
);
}
How can I do that?
any suggestions is helpfull.
Thanks
JavaScript arrays have a sort( ) method that sorts the array items into alphabetical order.
You can only get the name property out of all the values and sort it and then return it from service. Like below:-
Service Method:-
getNames(): Observable<string[]> {
return this.http.get(`API url`).pipe(
map((x:any) => x.records.map((item) => item.fields.name).sort())
);
}
Component: Just assign the method to an observable:-
export class AppComponent {
name$ = this.dataService.getNames();
constructor(private dataService: DataService) {}
}
In Component, template use the async pipe:-
<div *ngIf="(name$ | async) as result">
<ul>
<li *ngFor="let item of result">{{item}}</li>
</ul>
</div>
Demo with sample data:-
https://codesandbox.io/s/optimistic-tdd-dm898?file=/src/app/app.component.html
Update :-
if you have non-English alphabets in your string consider the below method for sorting.
getNames(): Observable<string[]> {
return of(APIReponse).pipe(
map((x) => x.records.map((item) => item.fields.name).sort((a, b) => {
return a.localeCompare(b, 'en', { sensitivity: 'base' });
}))
);
}
You can use just-sort-by - which is a very light library.
npm install just-sort-by
If your array is what is returned from the API call, then:
import sortBy from 'just-sort-by';
get communesFromService$(){
return this.apiService.communes$.pipe(
map((response) => sortBy(response, (item) => item.fields.name))
);
}

BotFramework-WebChat - Adaptive Card

Is there a way to add Onchange event to the adaptive card input field that is rendered in webchat (version V4). Example changing a quantity value (Adaptive card inputfield of type number) in the checkout screen should update the Total value (Adaptive card text field)
To keep it simple....In the below image once i change the number in the input box it should update in the below text box. everything should happen on the webchat V4(React) client side
Below are the options i tried, don't have any code to submit here:
option1: Tried to add an event to quantity input field in the card coming from the bot using middleware but not able to find an option to uniquely identify the input field to add the event (can see multiple input fields based on no of items in the card)
option2: create a new card in the frontend based on the card coming from bot and add events to that new card. Is it possible to interrupt the message going to bot and send a card from the frontend ?
option3: add an update button to the card so that the total is calculated in the backend and a update card is submitted to the user
below is the payload:
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0",
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"text": "Output",
"weight": "Bolder",
"horizontalAlignment": "Center",
"size": "Large",
"id": "output",
"color": "Good"
},
{
"type": "Container",
"items": [
{
"$data": "{items}",
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": " ",
"id": "line",
"spacing": "None"
},
{
"type": "Image",
"altText": "",
"id": "myimage",
"url": "{imgUrl}",
"spacing": "None",
"size": "Stretch",
"width": "1000px",
"height": "100px"
},
{
"type": "ColumnSet",
"id": "imgset",
"columns": [
{
"type": "Column",
"width": 50,
"id": "desc",
"items": [
{
"type": "TextBlock",
"text": "{description}",
"weight": "Bolder",
"spacing": "None",
"id": "desc",
"wrap": true,
"maxLines": 4
}
],
"spacing": "None"
}
],
"spacing": "None"
},
{
"type": "ColumnSet",
"spacing": "None",
"columns": [
{
"type": "Column",
"width": 50,
"id": "qty",
"items": [
{
"type": "Input.Number",
"placeholder": "Quantity",
"id": "myquantity",
"min": 0,
"max": 100,
"value": "{quantity}",
"spacing": "None"
}
],
"horizontalAlignment": "Left",
"verticalContentAlignment": "Center",
"spacing": "None"
},
{
"type": "Column",
"id": "pricec",
"items": [
{
"type": "TextBlock",
"text": "{price}",
"id": "pricet",
"horizontalAlignment": "Right",
"spacing": "None"
}
],
"verticalContentAlignment": "Center",
"horizontalAlignment": "Right",
"width": 50,
"spacing": "None"
}
],
"id": "qtypset"
},
{
"type": "ColumnSet",
"spacing": "None",
"columns": [
{
"type": "Column",
"width": 1,
"items": [
{
"type": "TextBlock",
"text": "Sub Total",
"size": "Medium",
"id": "subtotal00",
"weight": "Bolder",
"spacing": "None"
}
],
"id": "subtotal1",
"spacing": "None"
},
{
"type": "Column",
"width": 1,
"items": [
{
"type": "TextBlock",
"horizontalAlignment": "Right",
"text": "{subtotal}",
"size": "Medium",
"weight": "Bolder",
"id": "subtotalt0",
"color": "Accent",
"spacing": "None"
}
],
"id": "subtotal200",
"spacing": "None"
}
],
"id": "colsetsubtot00"
}
],
"id": "itemcontainer",
"style": "emphasis",
"spacing": "None"
}
],
"id": "rootcontainer",
"style": "accent"
},
{
"type": "ColumnSet",
"id": "totalset",
"columns": [
{
"type": "Column",
"width": 50,
"id": "totalcolumn",
"items": [
{
"type": "TextBlock",
"text": "Total",
"size": "Medium",
"isSubtle": true,
"weight": "Bolder",
"id": "total",
"color": "Dark"
}
]
},
{
"type": "Column",
"width": 50,
"items": [
{
"type": "TextBlock",
"text": "{total}",
"size": "Medium",
"id": "totaltext",
"horizontalAlignment": "Right",
"weight": "Bolder",
"color": "Accent"
}
],
"id": "totalcol2"
}
]
}
],
"id": "final"
}
I am using the below example as a starting point
https://github.com/microsoft/BotFramework-WebChat/tree/master/samples/04.api/e.piping-to-redux
webchat.js:
import React from 'react';
import ReactWebChat, { createDirectLine, createStore } from 'botframework-webchat';
import directLineDisconnect from 'botframework-webchat-core/lib/actions/disconnect';
import dispatchIncomingActivityMiddleware from './dispatchIncomingActivityMiddleware';
import uuid from 'uuid';
export default class extends React.Component {
constructor(props) {
super(props);
this.store = createStore({}, dispatchIncomingActivityMiddleware(props.appDispatch, this));
this.activityMiddleware = this.setActivityMiddleware();
this.attachmentMiddleware = this.setAttachmentMiddleware();
this.state = {};
}
componentDidMount() {
this.fetchToken();
this.setSendBox();
}
componentWillUnmount(){
}
async fetchToken() {
const myHeaders = new Headers();
const userDetails = uuid.v4();
myHeaders.append('Authorization', 'Bearer ' + 'mytoken');
myHeaders.append('Content-type', 'application/json');
const res = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', {
body: JSON.stringify({ user: { id: userDetails, name: userDetails }}),
method: 'POST', headers: myHeaders });
const { token } = await res.json();
console.log("My Token: " + token);
this.setState(() => ({
directLine: createDirectLine({ token })
}));
}
setActivityMiddleware(){
return () => next => card => {
return children => (
<div
className={card.activity.attachments && (card.activity.attachments[0].content.id === "output") ? card.activity.attachments && card.activity.attachments[0].content.id : ''}
>
{next(card)(children)}
</div>
);
};
}
setAttachmentMiddleware(){
return () => next => ({ card, activity, attachment: baseAttachment }) => {
let attachment = baseAttachment;
if (baseAttachment.content.body){
switch (baseAttachment.content.body[0].id) {
case 'review':
for (let i = 0; i < attachment.content.body[1].items.length; i++) {
attachment.content.body[1].items[i].items[3].columns[0].items[0].value = baseAttachment.content.body[1].items[i].items[3].columns[0].items[0].value.toString();
} //for loop
break;
default:
break;
}
}
return next({ card, activity, attachment });
};
}
setSendBox() {
this.store.dispatch({
type: 'WEB_CHAT/SET_SEND_BOX',
payload: { text: 'sample:redux-middleware' }
});
/*
this.store.dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: { name: 'membersAdded',
value: { language: window.navigator.language }
}
}); */
}
render() {
return this.state.directLine ? (
<ReactWebChat
activityMiddleware={this.activityMiddleware}
attachmentMiddleware={this.attachmentMiddleware}
directLine={this.state.directLine}
store={this.store}
styleOptions={{
backgroundColor: 'Transparent',
hideUploadButton: true
}}
/>
) : (
<div>Connecting to bot…</div>
);
}
}
dispatchIncomingActivityMiddleware.js:
export default function(dispatch, thisvariable) {
return () => next => action => {
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
const { activity } = action.payload;
if (activity.from.role === 'bot'){
var inputBox=document.getElementsByClassName("css-eycyw2");
if (inputBox.length > 0){
inputBox[inputBox.length - 1].style.display='block';
}
}
}
if ((action.type === 'WEB_CHAT/SEND_POST_BACK') || (action.type === 'WEB_CHAT/SEND_MESSAGE')) {
var inputBox=document.getElementsByClassName("css-eycyw2");
if (inputBox.length > 0){
inputBox[inputBox.length - 1].style.display='none';
dispatch(setInputVisibility(true));
}
}
return next(action);
};
}
The first thing to understand is that Web Chat uses the Adaptive Cards JavaScript SDK, available as an npm package. Web Chat mostly uses the out-of-the-box rendering functionality of the SDK, but one important thing it changes is how actions are handled. Without providing a customized handler, submit actions wouldn't be sent to the bot.
adaptiveCard.onExecuteAction = handleExecuteAction;
This is how applications are supposed to use Adaptive Cards. While most of the functionality is handled on the SDK side, there are a few things the application needs to do to make Adaptive Cards work for that specific app. While you can see Web Chat assigning a function to the onExecuteAction "event" property of a specific Adaptive Card instance, there is also a static counterpart of onExecuteAction that could be accessed like this:
AdaptiveCard.onExecuteAction = handleExecuteAction;
Using the static event will apply a handler for all Adaptive Cards instead of just one, but it will be overridden by any handlers applied to specific instances. The reason I'm telling you this is because there are many more static events, and there are a few in particular that will be useful for your situation:
static onAnchorClicked: (element: CardElement, anchor: HTMLAnchorElement) => boolean = null;
static onExecuteAction: (action: Action) => void = null;
static onElementVisibilityChanged: (element: CardElement) => void = null;
static onImageLoaded: (image: Image) => void = null;
static onInlineCardExpanded: (action: ShowCardAction, isExpanded: boolean) => void = null;
static onInputValueChanged: (input: Input) => void = null;
static onParseElement: (element: CardElement, json: any, errors?: Array<HostConfig.IValidationError>) => void = null;
static onParseAction: (element: Action, json: any, errors?: Array<HostConfig.IValidationError>) => void = null;
static onParseError: (error: HostConfig.IValidationError) => void = null;
static onProcessMarkdown: (text: string, result: IMarkdownProcessingResult) => void = null;
It would be possible for you to come up with a solution that uses the onInputValueChanged event, which fires every time any input in the card is changed. Your handler could search the card for other elements that it needs to use as operands for its calculation, and it would also need to search the card for the element that would display the result. Rather than doing all that work every time a character is typed, I prefer a solution that searches the card just once at the beginning for the elements it will use in its calculation. An alternative to listening to events on the Adaptive Card class or an Adaptive Card instance is to listen to events on particular elements, like inputs. So my example will use the static onParseElement event to get the elements it needs and then use the onValueChanged event for specific input instances it finds.
Before writing the code for the handler, we need to come up with a way for the code to know which elements to use for the operands and result of the calculation. For example, you could just have the code combine every input in the card (or in a container) and put the result in the last text block found. For my example, I've come up with a naming schema that the code can use. There are two keywords, "total" and "price", and the code looks for them in each element ID. I want to make it clear that this schema is totally arbitrary and that you can do something different if you want. Here's my example card:
{
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "TextBlock",
"text": "$10.00",
"id": "foo_a_price"
},
{
"type": "Input.Text",
"id": "foo_a"
},
{
"type": "TextBlock",
"text": "$2.00",
"id": "foo_b_price"
},
{
"type": "Input.Text",
"id": "foo_b"
},
{
"type": "TextBlock",
"text": "total",
"id": "total_foo"
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Submit"
}
]
}
You might be able to guess from looking at this that the idea is for one text block to have an ID that starts with "total_" and has some identifier after it. The quantities you want to add up start with that same identifier, and the price you want to multiply with each quantity has the same ID as the quantity but with the suffix "_price". I recommend using number inputs instead of text inputs, but this example shows that text still works. And here is the code for my example app that reads the schema:
import * as adaptiveCardsPackage from 'adaptivecards';
adaptiveCardsPackage.AdaptiveCard.onParseElement = element => {
const PREFIX_TOTAL = 'total_';
const SUFFIX_PRICE = '_price';
if (element.id && element.id.startsWith(PREFIX_TOTAL)) {
const itemPrefix = element.id.slice(PREFIX_TOTAL.length);
const card = element.getRootElement();
const inputs = card.getAllInputs().filter(input => input.id.startsWith(itemPrefix));
const products = {};
for (const input of inputs) {
const priceElement = card.getElementById(input.id + SUFFIX_PRICE);
const price = Number(priceElement.text.replace(/[^0-9.-]+/g, '')) || 0;
// `sender` will be the same as `input`.
// You could capture the input const instead of using the argument,
// but I'm demonstrating that you don't need to.
input.onValueChanged = sender => {
const quantity = Number(sender.value) || 0;
products[sender.id] = price * quantity;
const sum = Object.values(products).reduce((a, b) => a + b);
element.setText("$" + sum.toFixed(2));
element.renderedElement.replaceWith(element.render());
};
}
}
};
I have reason to believe that this change to the AdaptiveCard class will automatically be applied to the AdaptiveCard class in the package that Web Chat imports, since it's the same class in the same package. However, Web Chat now allows you to provide your own Adaptive Cards package as a property, so you can make sure Web Chat is using the package with your special event handler:
<ReactWebChat
directLine={createDirectLine({secretOrToken})}
adaptiveCardsPackage={adaptiveCardsPackage}
/>

Set next step for the waterfall dialogue in Microsoft BotBuilder NodeJS SDK

I am using Microsoft Bot Framework for my facebook messenger bot. I want to load the dialog data from json files instead of hard coding in the js file. I would like to configure the next step in the dialog, based on result from the "current" step, which is part of the json file configuration, something like this.
{
"name": "welcome",
"type": "waterfall",
"steps": [
{
"id": 0,
"data": [
{
"type": "text",
"value": "Hey, It's nice to meet you."
},
{
"type": "quickReplies",
"value": "What do you want to do next?",
"options": [
{
"text": "some option 1",
"value": "option1"
},
{
"text": "some option 2",
"value": "option2"
}
]
}
],
"next": [
{
"result": "option1",
"action": "goto step 2"
},
{
"result": "option2",
"action": "goto step 5"
}
]
}
]
}
I would like to process all the incoming messages and respond with correct dialog or correct step in the dialog for the user.
I am trying something like this;
handleMessage = function (session) {
var step = session.dialogData["BotBuilder.Data.WaterfallStep"] || 0;
// check response data from previou step and identify the next step.
// set the waterfall step id
session.dialogData["BotBuilder.Data.WaterfallStep"] = 2;
session.send("Hello");
}
var bot = new builder.UniversalBot(connector, function (session) {
handleMessage(session);
})
.set('storage',tableStorage);
With this code, I am always getting step as zero for session.dialogData["BotBuilder.Data.WaterfallStep"] even after setting this to a different number.
Also, as soon as I set the waterfall step number, all other state data that is stored in my table storage for this conversation is gone.
Storage data before setting waterfall step:
{
"BotBuilder.Data.SessionState": {
"callstack": [
{
"id": "*:/",
"state": {
"BotBuilder.Data.WaterfallStep": 0
}
},
{
"id": "*:welcome",
"state": {
"BotBuilder.Data.WaterfallStep": 1
}
},
{
"id": "BotBuilder:prompt-text",
"state": {
"options": {
"prompt": {
"type": "message",
"agent": "botbuilder",
"source": "facebook",
"address": {
"id": "mid.$cAAAlr-0LRH9niO21L1hV6hs83GuJ",
"channelId": "facebook",
"user": {
"id": "XXXX",
"name": "XXXX"
},
"conversation": {
"isGroup": false,
"id": "XX"
},
"bot": {
"id": "XXX",
"name": "XXX"
},
"serviceUrl": "https://facebook.botframework.com"
},
"text": "what do you want to next"
//ignored for simplicity
},
"promptAfterAction": true,
"libraryNamespace": "*"
},
"turns": 0,
"lastTurn": 1517594116372,
"isReprompt": false
}
}
],
"lastAccess": 1517594112740,
"version": 0
}
}
After I set the waterfall step:
{
"BotBuilder.Data.SessionState": {
"callstack": [
{
"id": "*:/",
"state": {
"BotBuilder.Data.WaterfallStep": 2
}
}
],
"lastAccess": 1517602122416,
"version": 0
}
}
Interestingly the step number is saved to the database (but in session state) but my "session" variable do not have this value anywhere. Also, even after configuring custom state service, the serviceUrl is still https://facebook.botframework.com which I thought is the default state service used if there is no state service set for the bot.
Per your code, as your bot actually contains only one waterfall step: handleMessage(session);, which raised your issue. You can consider to create multiple dialogs from json configration instead of complex waterfall steps.
Here is my quick test, for your information:
const json = `
[{
"name": "welcome",
"type": "waterfall",
"steps": [
{
"id": 0,
"data": [
{
"type": "text",
"value": "Hey, It's nice to meet you."
},
{
"type": "quickReplies",
"value": "What do you want to do next?",
"options": [
{
"text": "some option 1",
"value": "option1"
},
{
"text": "some option 2",
"value": "option2"
}
]
}
],
"next": [
{
"result": "option1",
"action": "dialog2"
},
{
"result": "option2",
"action": "dialog3"
}
]
}
]
},{
"name":"dialog2",
"type": "waterfall",
"steps": [
{
"data": [
{
"type": "text",
"value": "Hey, this is dialig2."
}]
}
]
},{
"name":"dialog3",
"type": "waterfall",
"steps": [
{
"data": [
{
"type": "text",
"value": "Hey, this is dialig3."
}]
}
]
}]
`;
const generateSignleStep = (step) => {
return (session, args, next) => {
step.forEach(sentence => {
switch (sentence.type) {
case 'quickReplies':
let choices = sentence.options.map(item => {
return item.value
});
let card = new builder.ThumbnailCard(session)
.text(sentence.value)
.buttons(sentence.options.map(choice => new builder.CardAction.imBack(session, choice.value, choice.text)))
let message = new builder.Message(session).addAttachment(card);
builder.Prompts.choice(session, message, choices);
break;
case 'text':
default:
session.send(sentence.value)
break;
}
})
}
}
const generatenextAction = (actions) => {
return (session, args, next) => {
const response = args.response;
actions.map(action => {
if (action.result == response.entity) {
session.beginDialog(action.action);
}
})
}
}
const generateWaterfallSteps = (steps) => {
let waterfall = [];
steps.forEach(step => {
waterfall.push(generateSignleStep(step.data));
if (step.next) {
waterfall.push(generatenextAction(step.next));
}
});
return waterfall;
}
var bot = new builder.UniversalBot(connector);
const jsonobj = JSON.parse(json);
jsonobj.forEach(dialog => {
bot.dialog(dialog.name, generateWaterfallSteps(dialog.steps))
.triggerAction({
matches: new RegExp(dialog.name, "g")
})
});
The result is:

AWS Security Group Creation NodeJS

I have a script that creates a security group(s) in AWS, it creates rules for ingress (inbound) and egress (outbound) traffic, my script looks like this right now:
#!/usr/bin/env node
/*
This is a script to generate security groups and apply them to instances in a VPC.
Attached to this script is a json file which has the security group parameters in it.
Run this script by executing:
node AWS_Security_Groups.js
*/
'use strict';
process.env.AWS_PROFILE
var PropertiesReader = require('properties-reader');
var AWS = require('aws-sdk')
var properties = PropertiesReader('/Users/testuser/.aws/credentials');
AWS.config.update({
accessKeyId : properties.get('aws_access_key_id'),
secretAccessKey : properties.get('aws_secret_access_key'),
region : 'us-east-1'
})
var ec2 = new AWS.EC2({apiVersion: '2016-11-15'});
// Load credentials and set region from JSON file
//AWS.config.loadFromPath('/Users/testuser/.aws/credentials');
// Load in security group parameters
let securityParams = require('./securityParams.json');
module.exports = {
//Exports creation of Security Groups
createSecurityGroup: (req, res) => {
ec2.createSecurityGroup(securityParams, function(err, data) {
if (err) {
return (console.log("Error", err));
}
// Pass the Json as a parameter in this function
ec2.authorizeSecurityGroupIngress(securityParams, function(err, data) {
if (err) {
res.serverError(err, err.stack);
} else {
res.ok(data);
console.log('Ingress Security Rules Created');
}
})
// Pass the Json as a parameter in this function
ec2.authorizeSecurityGroupEgress(securityParams, function(err, data) {
if (err) {
res.serverError(err, err.stack);
} else {
res.ok(data);
console.log('Egress Security Rules Created');
}
})
})
}
}
module.exports.createSecurityGroup();
My Json file looks like this:
{
"SecurityGroups": [
{
"IpPermissionsEgress": [],
"Description": "My security group",
"IpPermissions": [
{
"PrefixListIds": [],
"FromPort": 22,
"IpRanges": [
{
"CidrIp": "203.0.113.0/24"
}
],
"ToPort": 22,
"IpProtocol": "tcp",
"UserIdGroupPairs": []
}
],
"GroupName": "MySecurityGroup",
"OwnerId": "123456789012",
"GroupId": "sg-903004f8",
}
{
"IpPermissionsEgress": [],
"Description": "My security group2",
"IpPermissions": [
{
"PrefixListIds": [],
"FromPort": 22,
"IpRanges": [
{
"CidrIp": "203.0.113.0/24"
}
],
"ToPort": 22,
"IpProtocol": "tcp",
"UserIdGroupPairs": []
}
],
"GroupName": "MySecurityGroup2",
"OwnerId": "123456789012",
"GroupId": "sg-903004f28",
}]
}
However I can't get the script to execute properly. I keep getting an error saying unread character '/' in the JSON file. Does anyone know what I'm missing? Also, I want to be able update the script to read in the security groups and if the group already exists don't try and create it.
So this JSON seems to work to some extent: It doesn't create any rules, just creates the security group:
[
{
"IpProtocol": "string",
"FromPort": integer,
"ToPort": integer,
"UserIdGroupPairs": [
{
"UserId": "string",
"GroupName": "string",
"GroupId": "string",
"VpcId": "string",
"VpcPeeringConnectionId": "string",
"PeeringStatus": "string"
}
...
],
"IpRanges": [
{
"CidrIp": "string"
}
...
],
"Ipv6Ranges": [
{
"CidrIpv6": "string"
}
...
],
"PrefixListIds": [
{
"PrefixListId": "string"
}
...
]
}
...
]
I have to update my script with the params: securityParams[0].UserIdGroupPairs[0]

extjs4 jsonStore root

Help anyone, I need to use JSON data in my store. Can someone point what is wrong in my jsonstore config? I need to use the rain_value and air_pressure parameter in my graph but 'data' in JSON wont be set as root.
My device model:
Ext.define('device', {
extend: 'Ext.data.Model',
config: {
fields : [
{
name : 'dev_id',
type : 'int'
},
{
name : 'location'
}
],
hasMany : [
{
model : 'stationData',
name : 'data',
associationKey : 'data'
},
]
}
});
My deviceData model:
Ext.define('stationData', {
extend : 'Ext.data.Model',
config : {
fields : [
'dateTimeRead',
'rain_value',
'air_pressure'
]
}});
My jsonStore:
var store = new Ext.data.Store({
autoLoad : true,
model : 'device',
proxy : {
type : 'ajax',
url : 'dataURL',
reader: {
type: 'json',
root: 'data'
},
}
});
JSON Data:
[{
"dev_id": 171,
"location": "Sample location",
"province": "Sample Province",
"cell_num": "0123456789",
"posx": "longitude",
"posy": "latitude",
"elevation": "105",
"battery": "LP",
"region": "VI",
"type_id": "AWS",
"imei_num": "300234011463010",
"is_ftp": false,
"data": [{
"dateTimeRead": "2013-11-14 11:45:32",
"rain_value": "0.52",
"rain_intensity": "19.3",
"air_pressure": "1002.02",
"wind_speed": "7.9",
"air_humidity": "76.7",
"solar_radiation": "-305363.70",
"wind_direction": "327",
"air_temperature": "29.2",
"rain_cum": "238.07",
"soil_moisture1": "8.88",
"soil_temperature1": "28.7",
"soil_moisture2": "6.37",
"soil_temperature2": "27.6",
"rain_duration": "180",
"wind_speed_max": "14.1",
"sunshine_count": "0",
"sunshine_cum": "19286",
"wind_direction_max": "18"
}, {
"dateTimeRead": "2013-11-14 11:30:32",
"rain_value": "0.00",
"rain_intensity": "0.0",
"air_pressure": "1002.02",
"wind_speed": "8.9",
"air_humidity": "68.6",
"solar_radiation": "-239488.50",
"wind_direction": "322",
"air_temperature": "30.2",
"rain_cum": "237.55",
"soil_moisture1": "8.91",
"soil_temperature1": "28.5",
"soil_moisture2": "6.72",
"soil_temperature2": "27.6",
"rain_duration": "0",
"wind_speed_max": "15.1",
"sunshine_count": "455",
"sunshine_cum": "19286",
"wind_direction_max": "10"
}, {
"dateTimeRead": "2013-11-14 11:15:32",
"rain_value": "0.00",
"rain_intensity": "0.0",
"air_pressure": "1002.02",
"wind_speed": "10.2",
"air_humidity": "67.8",
"solar_radiation": "251642.70",
"wind_direction": "333",
"air_temperature": "31.0",
"rain_cum": "237.55",
"soil_moisture1": "8.90",
"soil_temperature1": "28.4",
"soil_moisture2": "7.01",
"soil_temperature2": "27.6",
"rain_duration": "0",
"wind_speed_max": "18.3",
"sunshine_count": "900",
"sunshine_cum": "18831",
"wind_direction_max": "9"
}]
}]