All posts by Matt Billock

Back to blog home page

Integrating Backand with JQuery

Posted by on Mar 28, 2017

JQuery is the most widely-deployed JavaScript library. It provides a comprehensive set of responsive tools for dynamically working with an HTML page’s DOM, and is responsible for establishing and guiding a lot of the patterns that are the standard in web development. In this article, we’ll look at how you can integrate the Back& Vanilla SDK with an app built with JQuery.

Note: The code for this article is available on CodePen.

Including and Initializing the SDK

The first step in connecting your JQuery-based web app to Backand is to include the JavaScript file containing the Backand SDK. You can either do this through a package manager, or via our CDN.

Creating the Service

Once you’ve included the SDK, you’ll have access to the functionality via the global object backand. To integrate Back& with a JQuery-based web app, we’ll make use of a global service variable that will wrap the SDK. This service will handle initialization of the sdk, and provide all of the tools necessary to work with a Backand application. We’ll start by declaring the service and initializing Backand with the following code:

This defines an object – dataService – that can be used to interact with the Backand SDK. In the data service, we define a method – init – that is used to initialize the SDK’s connection to a Backand application. Simply call dataService.init() at the approrpriate location in your app’s initialization code, and the SDK is ready to use (in the example, we do this in the $(document).ready handler, but this is not required).

This post uses a pre-built demo app, reactnativetodoexample, and its associated keys. If you wish to connect this demo to your own Backand application, simply replace appName, signUpToken, and anonymousToken with the correct values from your Backand application’s dashboard. Aside from updating the data model to include the ToDo object, the only thing you will need to do to ensure your app operates in the same way is to add the relevant custom actions when editing a ToDo item (see below).

Fetching and Manipulating the Data

Now that our app is wired up to Backand, we’ll want to start writing code to work with our Backand application’s objects. We can retrieve a list of “todo” objects from the server by adding a property to the dataService object, getList, that calls the appropriate SDK method:

This function first defines a set of filter params using helper methods in the SDK (refer to our documentation for more information). It then calls backand.object.getList() to fetch the list of objects in the SDK. The result of this call – a promise – is then passed back to the calling code. The calling code can then create its own success and failure handlers, using .then and .catch, and update internal state appropriately.

From this point onward, working with the SDK is a matter of writing wrapper methods in the dataService object for each SDK method with which you wish to interact. Backand provides interfaces for all of the common database manipulation tasks. For example, the following call defines a method – create – that creates a new entry in the database:

You can use this technique to wrap every call in the Backand SDK, providing your JQuery code with full access to the server.

Handling Responses

All Backand SDK methods return a promise, performing the tasks requested asynchronously. To handle the responses, we simply need to write the appropriate handlers for the .then and .catch calls. In the code below, for example, we write a simple set of handlers for the getList call:

As JQuery is a very flexible framework, it is hard to provide concrete guidance on exactly where SDK should take place in terms of the program’s execution. The CodePen example demonstrates one method of creating this component, but you do not need to mimic the code structure there exactly. The key component is to ensure that backand.init() is called prior to any SDK calls taking place, otherwise the calls from the SDK will fail.

Conclusion

With the above code, you now have a simple data service that you can use to update your JQuery-based app’s user interface with results from a Backand application. You can make full use of the SDK, with the capability to create, update, and delete records at will in a responsive manner, no server code required. With Backand, you can accelerate your JQuery front-end development, focusing on what makes your app unique, and leave the server side to us.

For more information, visit Backand  and check out our code samples, tutorials, and more.

Back to blog home page

Deeper Mandrill integration using AWS Lambda

Posted by on Mar 21, 2017

Mandrill is a transactional email API built by MailChimp to facilitate programmatic communications with your app’s users. While we already have some demonstration JavaScript for sending a plain message via Mandrill, supporting attachments needs a server-side Node.JS Action. Below, we’ll create a server-side Node JS action (powered by AWS Lambda) that imports the Mandrill SDK, allowing you to easily send email with attachments through Mandrill in your Backand application.

Getting Started in Backand

To get started with a Mandrill action, you’ll need to first create a new Server-Side NodeJS action in Backand. To do so, open up your app’s dashboard at https://www.backand.com, and navigate to an object that will host the action. On the Actions tab of this object, create a new “On Demand” action. Name this action, then select “Server-Side Node.JS Action” as the action’s type. Follow the documentation provided to download the Backand CLI and initialize your action locally.

Backand’s Server-Side Node.js actions are implemented as Amazon Lambda functions. These functions are built using Node.js, and deployed as you would any other Node application. Backand provides a convenient CLI for managing these actions (documented in the action description), as well as easy-to-use tools to trigger execution of the Lambda function from the REST API.

Initializing the Mandrill Action

Once you have the Server-Side Node.js action ready to go, the next step is to create an action in Mandrill. Navigate to http://www.mandrill.com/ and create a new action in the MailChimp dashboard provided when you log in. Once the action is ready, copy down the Mandrill API Key value and save it somewhere safe. This API key will be used by the Node.js action to connect and communicate with Mandrill.

Updating the Code

Next, we’ll need to update the action’s code to communicate with Mandrill. We’ll start by configuring the mandrill SDK requirements in index.js. Add these lines into index.js in your Node.js action folder structure, before the function exports.backandCallback:

Note: Be sure to replace MANDRILL_API_KEY with the value copied down from the Mandrill dashboard.

Next, we’ll modify the backandCallback function to send a message with Mandrill. Replace the contents of this function with the following code:

This code does two things:

The initial code fetches the attachment data from the server on which it is stored. If this code succeeds, it calls sendEmail with the file data provided as a Base 64 string.
sendEmail then takes this data and contacts Mandrill to send the message.

Configuring the Code

To tie the project together and finalize the above code, simply replace each of the placeholders with the appropriate value:

  • PATH_TO_ATTACHMENT_INCLUDING_FILENAME – this is the URL to the attachment you wish to send. If fetching this fails, sending the message will not succeed.
  • FILENAME_OF_ATTACHMENT – this is the file name that will be given to the attachment.
  • RECIPIENT_EMAIL – This is the email for the message’s intended recipient.
  • RECIPIENT_DISPLAY_NAME – This is the recipient’s display name.
  • SENDER – This is the email from which the message was sent.
  • EMAIL_SUBJECT – This is the subject of the email.
  • MESSAGE_BODY – This is the content of the email.

You can also use the parameters argument to the action to send additional message data, whether that data originates in your app’s database or via the API call. Once these changes are made, your server-side action is ready to deploy!

Testing and Deployment

You can debug and run the action locally using the provided debug.js file. Simply enter node debug.js on the command line to debug. Once you’ve finished your local testing, you can then deploy the action via the documentation provided in the Server-Side Action’s UI in your app’s dashboard at Backand.com – head to the Actions tab for the relevant object, and follow the instructions on using backand action deploy to deploy your code.

Calling the Action

To call the action in your client-side code, simply use the Backand SDK’s action functionality as you would any other on-demand action:

Simply replace OBJECT_NAME with the name of the object controlling your action, and ACTION_NAME with the Server-Side Node.js Action’s name in Backand. You can provide any extra information or detail using the provided parameters object – this will be passed into the parameters argument of your action.

Conclusion

Many apps need to send additional supporting documents along with their transactional messages, such as images used in the message or related files. With the above Server-Side Node.js Action, you’re given the full capability necessary to enhance your transactional messages with any attachments you see fit. Learn more about Backand’s Server-Side Node.js actions in the documentation, or make use of our previous example on sending messages without an attachment from a custom JavaScript action.

Back to blog home page

Integrating Backand with Ionic Creator

Posted by on Mar 14, 2017

Ionic Creator is an online IDE for Ionic apps that can greatly speed the development of your cross-platform mobile and web application. However, like any IDE, it can pose problems when integrating with some external service providers like Backand. Below, we’ll look at the steps needed to get your Ionic Creator app up and running with the Backand SDK, allowing you to implement serverless apps in the Ionic framework with ease.

Configuring the Connection to Backand

To Enable Back with, you’ll need to update your Ionic Creator app’s “Other JS” section to include a new file name – bkndconfig.js. In this file, replace the entire contents with the following code:

Once the code has been modified, replace the values above with the appropriate values from your Backand application:

  • BACKAND_APP_NAME – This is your app’s name in Backand
  • BACKAND_API_SIGNUP_TOKEN – This is your app’s signup token. It is available in the ‘Security & Auth -> Social & Keys’ section.
  • BACKAND_ANONYMOUS_ACCESS_TOKEN – This is your app’s anonymous access token. It is available in ‘Security & Auth -> Configuration’.

Once these changes have been made, you’ll need to update your application’s code settings.

Code Settings

Next, we’ll update the app’s Code Settings to import the Backand SDK. In ‘Code Settings’, under the ‘External JS’ tab, add these two script URLs:

Once you’ve finished, the External JS tab will have the following content:

Next, under ‘Angular Modules’, add ‘backand’ and ‘app.config’. The end result will have the following content:

Working with the Backand Provider

Now that the external code has been configured, you can start working with the Backand provider in your service class. For example, you can use the following code to pull rows from an ‘items’ object in a Backand application:

Next, we’ll add this function call into a page for display.

Updating the Page Controller

To access this data, we’ll update the page controller to call our ItemsModel and return the relevant rows. To do so, use the following JavaScript to define a getAll() function and store the results in $scope:

Now, we’ll need to update the page’s design to display the new information. To show a list of all the items you’ve obtained:

  • Drag a “List Item” element onto your UI
  • In the page list pane (upper left hand corner), click on ‘List’
  • On the right side of the pane, click on ‘Angular Directives’ and add a new directive with the following details:
    Directive: ng-repeat
    Value: object in data
  • Finally, click on the ‘list-item’ element and change the content to {{object.name}}

And with that, your Ionic Creator app is now connected to Backand!

Learning More

At this point, you have the full power of the Backand SDK available in your Ionic Creator app. You can use the SDK to add more services to your app, providing CRUD functionality, real-time communications, server-side code execution, and more! Simply head over to our documentation to get started.

Back to blog home page

Deep Dive Series: Integrating Backand with Ionic Creator, Mandrill & More

Posted by on Mar 14, 2017

Over the next several days, we’ll be running a set of deep-dive blog posts. Each post will cover a different topic in-depth, providing a deeper look at integrating Backand into a number of different application development scenarios. The posts will cover three differing topics, showing a range of functionality that can be implemented into a serverless app. A brief overview of each topic is provided below, to serve as a reading guide.

Integrating Backand with Ionic Creator

Ionic Creator is a powerful online development environment for Ionic apps. While it provides a convenient, UI-focused means for implementing a multi-platform application, it can make some tasks – such as integrating with a third-party service provider like Backand – challenging. In the article on integrating with Ionic Creator, we’ll cover the steps necessary to get Ionic Creator working with our new SDK. By following the steps in this article, you’ll be able to make your apps truly portable through the use of our serverless platform and Ionic’s core offering of cross-platform capable and consistent user interfaces.
Read more

Sending Email Attachments with Mandrill

While we can easily send email messages over MailChimp’s Mandrill API with simple HTTP calls, any message that includes an attachment needs to be created on the server side. In the Mandrill Attachments article, we’ll cover the process for creating a Node.JS Server-Side JavaScript action that can add an attachment to a transactional email sent via Mandrill. We’ll cover the JavaScript code you need to fetch a file from the web, convert it to a format Mandrill can accept, and then initiate the message send.
Read more

Batch and Bulk Processing in Backand

Backand’s automated REST API generation provides developers with a significant amount of functionality right off the bat, allowing your app to quickly begin working with the objects represented in your application’s database. However, these APIs are limited to working with a single record or retrieving a list of records. If you wish to create multiple records at once or update several objects across multiple database tables in a single API call, then you’ll want to make use of our bulk processing API. We’ll provide all of the details you need to create bulk processing requests, allowing you to easily update large numbers of records in your application’s Backand database.
Read more

Other topics

We’re always looking to help developers understand how to better work with the Backand platform. Our goal is to make developing your app easier, and we’d love to hear from you if you have an idea that would help us achieve that goal. Simply contact us via social media, or using our contact form to start a conversation!

Back to blog home page

Integrating Backand with Vue.js

Posted by on Mar 07, 2017

Vue.js is a progressive framework designed to provide an intuitive means by which a front-end developer can build their application. It is designed to be incrementally adaptable, allowing you to use it as often as you like, and provides a lightweight set of tools for adding dynamic functionality to a web app. By providing dynamic backing to the view model, you can easily make a Vue.js-based application into a large and feature-rich web app. In this post, we’ll look at how we can use Backand’s Vanilla SDK in a Vue.js-based application to provide the backing for that data service.

Note: This example is also available on Codepen. If you don’t yet have a Backand account, you can get on for free, here.

Including and Initializing the SDK

The first step in connecting your Vue.js app to Backand is to import the JavaScript file containing the Backand SDK. You can either do this through a package manager, or via our CDN.

Once you’ve included the SDK, you’ll have access to the functionality via the global object backand. You’ll next use this to initialize our connection to Backand in the constructor for your app’s Vue instance. We recommend putting the initialization in the beforeMount lifecycle hook, but any location or hook that initializes the SDK before it is used should be sufficient:

 

With that, the Backand SDK is initialized. This post uses a pre-built demo app, reactnativetodoexample, and its associated keys. If you wish to connect this demo to your own Backand application, simply replace appName, signUpToken, and anonymousToken with the correct values from your Backand application’s dashboard. Aside from updating the data model to include the ToDo object, the only thing you will need to do to ensure your app operates in the same way is to add the relevant custom actions when editing a ToDo item (see below).

Loading the object data

Next, you need to define a global method that knows how to load the object data from your Backand application. To do this, first define an empty data member to store the future results from the API:

This data member will hold the list of ToDo items obtained from the server. Next, we’ll define a global method to populate this data member:

This function first clears out the todos data member, then defines a set of filter params using helper methods in the SDK (refer to our documentation for more information). Then, it calls backand.user.getUserDetails() to determine if the list should be restricted to the currently logged-in user. Finally, the method calls backand.object.getList() to fetch the list of objects in the SDK, and then updates the todos data container in the promise resolution block.

Now, you simply need to call fetchTodos() in the appropriate location in the Vue lifecycle. To load the data when mounting is complete, you can add it to the “mounted” handler like so:

Creating, Editing, and Deleting Objects

Once you’ve got the basic display up and running, you’ll want to add methods to create, update, and delete the ToDo items based on user actions. The following code is a handler for an x-template component titled todo-form, which is used to create a new ToDo entry in the application:

Following a similar pattern to the initial load, this code first checks for a user context. If a user is not logged in, this call will have a value of null. Otherwise, we can use the userId of the active user to assign ownership of the new ToDo item. The code then calls backand.object.create() to create the record in your Backand application.

You can follow a similar pattern when updating an object:

This function demonstrates how to update a ToDo item’s completion date using backand.object.update() – simply provide the object name, the object ID, and the collection of changes to be made. The above method also provides deletion functionality with the remove method. Simply provide the object name and the ID of the ToDo item to delete the entry from your database.

Keeping the app up-to-date

While you now have a fully-functional CRUD interface for the “todo” object, you have not yet built a way for your application to receive – and respond to – updates. You can accomplish this using Backand’s Realtime Communications capability. First, create the appropriate Custom JavaScript actions in your Backand app’s dashboard, and have them emit an event titled “update_todos” as follows:

Once that’s done, we need to set the SDK to enable socket mode:

Finally, you need to add an event handler. Update your mounted handler to include the event handler function as follows:

This function handles all update_todos events the same way – by reloading the entire list from the server. You can use a similar technique to write different handlers for each database operation, or to perform other types of logic based on your application’s needs.

Conclusion

With the above code, you now have a simple data service that you can use to update your Vue.js app’s user interface with results from a Backand application. You can create, update, and delete records at will in a responsive manner, no server code required. With Backand, you can accelerate your Vue.js development, focusing on what makes your app unique, and leave the server side to us.

Back to blog home page

Upgrade Your Apps to Work with Our New Serverless SDK

Posted by on Mar 02, 2017

This post is a quick guide to upgrading your existing Backand-powered applications to work with the new Backand Serverless SDK we announced a couple of weeks ago. We’ll cover the changes made in the SDK, and explore the steps you need to take to get your Backand application up and running with the new API.

What’s changed?

We’ve made a number of changes in our new SDK that make developing against Backand faster than ever. Here’s a brief overview of what changed:

  • Streamlined authentication, making it easier for you to authenticate your users.
  • Improved the process for registering with – and signing in via – social media providers with the addition of new SDK methods.
  • Greatly improved the method in which you interact with Backand objects. Gone are the days of needing to manually construct a URL – simply specify the object name as part of a clearly-named function call to the API, and we handle the complexity for you.
  • We have also streamlined our response data, allowing you to work with the results of calls to your Backand back-end in a more straightforward manner.

And on top of all this, we continue to offer the same secure, scalable, and responsive backend-as-a-service that we always have!

Converting old app code to the Serverless SDK

Next, let’s look at what steps are needed to change a Backand application to work with the new SDK. For reference, we are basing this code off of our base Ionic starter app – see it on the Ionic marketplace at https://market.ionic.io/starters/backand-simple.

Step 1: Add new SDK to bower.json

First, you’ll need to add the new SDK to your bower file.

Step 2: Update Script includes in index.html to include the new SDK.

Start with removing the old SDK by commenting out – or deleting – the following line:

Then, replace it with the new SDK links:

Or, use the CDN files:

Note: You may want to check your dependencies directory for any lingering versions of the old Backand SDK – this can cause cache issues due to competing HTTP Interceptors. Simply remove the old folder from the appropriate directory in your project.

Step 3: Update the user authentication method

User authentication is largely unchanged, with a few notes below:

  • If you have previously disabled anonymous auth in your app, you can re-enable it using Backand.useAnonymousAuth(). This value defaults to true, so if you do not modify the default value you do not need to make any changes.
  • We’ve added new social media authentication functions: socialSignin, and socialSignup. Please note that while some of the examples in our github repos – and some of our documentation – used socialSignUp to refer to the specified function in the Backand SDK, this is no longer valid – the “up” will not be capitalized in the new SDK.

Step 4: Update the method used to detect unauthorized users

The getToken() method has been expanded, to use a promise. Originally this method would return undefined when a user was unauthorized, but this functionality can now be managed via the promise method. In the new SDK, the getToken() method is not as prominent as it was in previous versions, and you are likely to not need it as you work on your app.

Step 5: Change $http usage to use the Backand SDK REST methods:

The Serverless SDK features wrapper methods that can be used in place of the direct HTTP calls used in the previous method. Here’s a quick comparison of the old SDK’s HTTP communications, as compared to the new function-based Serverless SDK:

Step 6: Update data result usage

The old SDK would use code similar to the following when handling responses from Back& API calls:

The new SDK does not store the results in a nested data member, but rather in a root data element. The old SDK stored the response contents in a root property, meaning that the actual data was a subset of this data property – hence the use of result.data.data in the old SDK. With the new SDK, you no longer need the extra level of disambiguation, and can store the data in your application’s object with the following code:

Conclusion

In this post, we covered the Backand Serverless SDK. We touched briefly on the features the new SDK has to offer and walked through the process necessary to convert an existing Backand-powered app to the new API. Most importantly, though, is that we built this SDK based on your feedback, and we want more! Connect with us via social media (Twitter, Facebook), or on StackOverflow, or even directly via comments on our Git repos and contacting customer support – we’d love to incorporate your thoughts and suggestions into the next version.

Don’t have a Backand account? You can get one for free

Back to blog home page

Protecting Your Apps Against the Next AWS Outage

Posted by on Mar 01, 2017

On February 28th, Amazon Web Services had several issues that led to global outages in the AWS stack. This started at around 9:40 AM Pacific time, and was resolved by 2:00 PM Pacific. During this outage, many popular websites faced availability and functionality issues, as more than 30% of the web relies upon AWS for some aspect of its functionality. While major AWS outages are rare, when they happen they can be devastating. As such, we wanted to explore why the AWS outage was such a problem for many websites, and look at mitigation strategies to prevent the next failure.

All Your Functionality Under One Roof

One of the benefits of using AWS to manage your web application is that it gives you access to all of the functionality you need to provide scalable and robust storage. Let’s take a look at a sample architecture for a Ruby on Rails web application, built on top of AWS:

  • Domain name resolution resolves to an AWS Elastic Load Balancer (ELB)
  • The AWS ELB directs the traffic to one of several Elastic IPs
  • The Elastic IPs point at an Amazon EC2 instance running a popular web server, like nginx or Apache, which runs the application server code
  • The application server interacts with a database in Amazon RDS or Amazon DynamoDB to store application data
  • The application can also interact with Amazon Elasticsearch Service to ease searching and record location
  • The application can also integrate with a third party logging or user action tracking provider (which has a high probability of also being deployed on AWS)

This combination of tools gives Developers and DevOps engineers a powerful set of functionality that they can use to build, maintain, and deploy their web applications. By having everything in one location, you can reduce the knowledge load on your R&D and DevOps teams, increasing overall velocity while relying upon the sterling reputation of AWS to provide you with a measure of security that you’ve made the right choices in your architecture.

The Problem

The problem with the above should be obvious. If your app is built entirely on AWS, any outage – no matter how minor – has the chance to affect your application’s user base. For example, if the ElasticSearch service goes down, your users may no longer be able to search for products in your catalog, losing you sales. Or if an EC2 instance behind a load balancer goes down, you can lose current session data, causing a user to have to restart a purchase process. While even minor outages can have an effect, the problem faced is the compounding that occurs when two or more AWS services go down at once. During the recent outage, Amazon itself was unable to effectively update its own status dashboard.

So at this point, we can easily demonstrate that while having everything under one roof can provide development speed and maintenance gains, it does expose your app to risks of cascading failures. Tuesday’s outage led to popular communications providers like Slack experiencing issues with uploading and sharing files, for example. When the web is this interconnected, it pays to have backup plans. Let’s look at two different mitigation strategies – one within AWS, and one leveraging multiple providers.

Mitigating Disaster Within AWS

Tuesday’s failure was primarily located in the US-East-1 region. This region is often used by Amazon to roll out and test new features, and posters on HackerNews have reported that this region has historically had issues around new software rollouts and old hardware. Many DevOps engineers will make use of a single region to ease maintenance and support of a website, and when you are front-line support at a startup this kind of region usage is often sufficient to get things moving so that your engineers can move onto more pressing business problems. However, if you host all of your services within the same AWS region, you are highly susceptible to that region’s stability issues, and when it goes down you lose access to your entire application. So the most obvious strategy is to not restrict your application to a single AWS region. Simply choose one of the other AWS Regions available to your account, and change your internal practices to recognize this difference.

Another strategy is to duplicate your stack across multiple AWS regions and use other regions as a fallback for when a region goes down. Configuring a load balancer to switch between EC2 instances hosted across separate AWS regions can often mitigate the failures that occur when a single AWS region is unavailable. This is even supported as a feature in Amazon ELB. By dispersing your risk among different AWS regions, you can reduce the overall impact of a single region’s failure.

Diversifying Your Risk

While spreading your components across multiple regions can provide you with a more reliable recovery scenario when things go wrong, they do ignore one major potential risk – if the entirety of AWS is unavailable, it doesn’t matter what region your app is located in. This is the risk with keeping all of your technical infrastructure under one vendor. While Amazon takes excellent, pro-active steps to ensure that their infrastructure is redundant and can failover gracefully, you’re still subject to issues that affect the AWS stack as a whole. To fully prevent this kind of outage, you’ll need to work on exposing alternatives at each point in the tech stack:

  • For load balancing, you’ll need to find another provider for load-balancing your app. This can be as straightforward as using another provider’s load balancer (such as Google Cloud Load Balancing), or as complex as building your own load balancer using proprietary tools.
  • For machine hosting, again you’ll want to look at alternatives to EC2 (or Amazon Lambda, for serverless applications). These can be other cloud service providers like Microsoft Azure (which can also serve as a replacement for container services like Heroku, since many run on AWS), or even machine hosting providers like Rackspace.
  • For data storage, you have a variety of options. Plenty of other cloud providers give you access to a scalable and reliable database accessible from everywhere, but you can also build your own DB server using a service like Rackspace to provide hosting for your DBMS.

The key is that for every element of your tech stack, there is an alternative to AWS that can serve your needs just as well. Ensuring full resilience and availability of your application requires spreading your risk across multiple providers, and handling the case when one (or many) of these providers fail.

Conclusion

If you’re running a web application, the availability of your app is paramount to your project’s success. Keeping everything under one roof with a provider like AWS gives you the ability to focus exclusively on one stack, letting your engineers be more efficient, but it also exposes you to risks of downtime across the relevant service. While high-availability options can be expensive, if your sole source of revenue is traffic to your website then you are ultimately faced with the following question: how much is not having downtime worth to me? If you’re willing to sacrifice time, effort, and money to ensure you have alternatives at every level of your app’s tech stack, then you can easily write a high-availability application that you can feel confident will survive most outages.