-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: support recursive uischema definition #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
* extract visual editor from design page * bump shared packages * add botframework-ui dependencies * extract property editor from design page * don't pass rootPath as a prop * [wip] use adaptive form package * don't hoist botframework-ui packages * start stubbing out sync based new shell api * bump prompts plugin * revert to using shell extension container to render new form * add extension and adaptive form packages * update jest config to include display name * import json and prompts plugins * convert code editor to @monaco-editor package * render string field for expressions * remove height transition when there is an error * feat: Added select dialog ui plugin (#1) * feat: Added select dialog ui plugin * Apply suggestions from code review * Added tests * Clean up * Updated tests * renamed directory to select-dialog Co-authored-by: Andy Brown <asbrown002@gmail.com> * enable json ui plugin * allow customization of $role and $kind fields * [wip] begin lg editor field * fix height calculation * fix: Set default value in StringField to an empty string (#2) * use field name for lg fields * ignore lg lib * refactor plugin config add roleSchema and kindSchema * clean up lg plugin dependencies * drop ui prefix for ui options * bump @types/react and @types/react-dom * initial support of recognizers in plugin config * initial lu ui plugin * fix: Fixed object array border conflict (#3) * fix: Fixed object array border conflict * Modified getUISchema function * Resolve build errors * Fixed uiOptions in SchemaField * Reverted changes to getUISchema * [wip] start work on rendering better descriptions and help links * finish rendering description and help links * re-renable custom keybinding * clean up old bfcomposer monaco editor bits * fix build errors * do not enable minimap by default * feat: Add Select Dialog Plugin * Updated defaultUiSchema * Added select dialog plugin * fixed issue in uiSchema * Fixed luIntentOptions * Added help link * Modified tests * Fixed tests * move shared types from indexers to shared * fix merge issue * fix lint error * do not compile with test files * move prompt settings to 'Other' tab * fix: Moved prompt settings to 'Other' tab * fix: prompt settings styling and description * wrap labels in formatMessage Co-authored-by: Andy Brown <asbrown002@gmail.com> * move globally hidden fields to getOrderedProperties * [wip] add ability to have multi field rows * make sure to pass value to form rows * use json schema version 7 for base typings * add one of field * begin work on expression editor * update prompt plugin to use json schema v 7 * feat: Updated FormRow to support multiple fields in a row * feat: Updated FormRow to support multiple fields in a row * Updated FormRow * Updated FormRow * fix problem with yarn install * bump react versions * fix merge conflicts * correctly hide placeholder in code editor * remove inline recognizer editor * remove select intent plugin * add key to editor to force remounting * handle type and oneOf expression schemas * port inline luis intent editor * memoize initial value * rename lu plugin to luis * port regex intent editor * fix: Moved type selector to be inline with label (#7) * fix: Fixed warning related to link navigation styling (#8) * rename handleChange to handleRecognizerChange * add method to recognizer config to determine if selected * refactor code editors to not have dependency on monaco-editor package directly * downgrade monaco-languageclient There is an error initializing the language services * export OnInit type * clean up monaco dependencies * update sample schema * render all other fields on single row sets up support for defaultValue exression editor * render json editor with selected schema for complex types * feat: Added custom array field for set properties * feat: Added stackArrayItems to UiOptions * fix: Fixed array item borders * add designerId to ShellData formContext.dialogId is actually data.$designer.id which is confusing when there is dialogId in the ShellData. * feat: Updated ObjectArrayField to display complex arrays in a stacked layout * fix: Filtered object array properties to reflect ui order * removed console.log * fix: Fixed ExpressionField flicker * feat: Added add button for stacked array items * add schema prop to json editor * updated defautUiSchema.ts * register schemas on a per-model basis * fix merge build issue * trigger intent field change when editor changes * fix: Added 'expression' to expression type dropdown. * add disabled item actions for new array item * reduce height of lg field * pass raw errors to schema field child * plumb errors through the prompt form * fix array item styles when an error is present * use label overrides from ConceptLabels in form title * pass project id to lg editor * fix: Added 'expression' to type selector * implement arrayitem wrapper for array fields * fix: Added helpLinks to defaultUiSchema * add useArrayItems hook for array fields * fix: Added 'expression' to type selector * fix: added 'expression' to type dropdown * fix: combined 'selectedSchema' and 'selectedKey' into 'selectedOption' * feat: Added inline lu to prompts * do not override descriptions for prompt fields * fix array item usage * support expression fields with object properties * add subtitle to uiSchema * add titles to choices and confirmChoices * do not use label overrides in form title * [wip] use schema field to render all input fields * update ui schema * correctly set selected option * fix: fixed array item styles * feat: added formatData to uiOptions * fix: Added formatData to ObjectArrayField * default to correct type in expression editor * start case the fallback labels * allow labels for objects with properties * combine choice input and confirm input components * handle enum expressions * feat: Removed formatData in favor of serializer * fix: Fixed serializer labeling * Updated jest.config.js * fix: fixed recognizer in prompts * fix: fixed getSelectedOption returning expression option for strings * fix unit tests * Removed select intent * Fixed luis handleRecognizerChange method * fix merge issue * fixed error message displaying next to field in an array * removed kindSchema comments * update error message when recognizer can't be determined * add testid to expression dropdown * fix todo spec * fix notifications page spec * remove obiformeditor package * fix new LGTM alerts * incorporate locale when setting lg file name * fix selecting correct lu file with locale * sync data with shell to support undo/redo * update oneOf field to handle array of types * do not use transparent border for one of fields * do not have transparent border for expression editor It looks weird with the prefix icon * fix: Fixed dropdownWidth in expressions * fix: Fixed inline array styling * fix: removed unused variable * fix: Fixed lint issue * get localize lu file for inline intent editor * fix: Fixed error rendering issue * fix: Fixed schema field styles Co-authored-by: TJ Durnford <tjdford@gmail.com>
LivingSocial API Design GuideVersion 1.0.0 - Published 2017 Jul 28Internal version published 2016 Mar 10 Contents
PurposeThe purpose of the LivingSocial API Design Guide is to provide A basic knowledge of REST, HTTP, and JSON is assumed. We will provide OverviewAPI Design has come of age, and has become a first-class citizen in the Why Have an API Design Guide?An API Design Guide provides us with the following:
As we begin to externalize our APIs, they reflect on our company, and This is a living document and subject to change and discussion. Please Guiding PrinciplesHere are the guiding principles for designing an API and determining
API Design EssentialsThe remaining sections of this page cover the key areas to consider RequestsURIs/PathsThe URI (Uniform Resource
Use Plural Resource NamesResource names should be plural, for example if we're exposing unless the resource is a singleton, for example, the overall status of Use Nouns, Not Verbs in the Base URINever put a Verb in the Base URI. Rather than something like We'll cover appropriate HTTP Verb usage in the HTTP Verbs section below. Shorten Associations in the URI - Hide Dependencies in the Parameter ListResources are related to one another, and eventually they are stored in a database with associations between the tables. In the early days of REST, there were URIs that looked like: This is bad practice because:
Instead, shorten the association in the URI and hide the nested data dependencies in the parameter list. Most URIs should go no deeper than the following: With this structure in mind, the new path now looks like: Uniform Interface with HTTP Verbs/MethodsThe following table shows the standard HTTP Verbs/Methods to act on resources. Use this approach rather than cluttering the URI with verbs.
Resource IdentifiersResources should use Sequential UUIDs (SQUUIDs). We chose SQUUIDs because they limit fragmentation of indexes and eliminate performance problems in MySQL. SQUUIDs are stored as binary(16) SQUUIDs should be represented in string form as lowercase. This is so that they can easily be joined across tables in the data warehouse. Do Not Use Database Table Row IDs as Resource IDsAuto-incrementing integer database row identifiers should not be used as Resource IDs or exposed in any way through the API. Exposing DB table "auto-increment" row IDs as Resource IDs leaks implementation details outside the API. Row IDs couple the resource too closely to the underlying persistence engine and schema, and can cause headaches when the owning service might need to migrate away from a particular persistence engine or structure, or when clustering of several DBs becomes necessary. IDs not generated by autoincrementing column IDs allow for more flexibility. Sample SQUUID GeneratorVersioningWhy Versioning?API Versioning is an important aspect of API design because it informs the consumer about an API's capabilities and data. Consumers use the version number for compatibility. Versioning MethodsHere are 2 of supported methods of API versioning:
We currently have some APIs that use Header-based versioning, and some that use URI-based versioning, in addition to deployed mobile apps that invoke URI-versioned APIs. Here is the direction we would like to take:
Version in the HTTP
|
| HTTP Response Header | Description | Example |
|---|---|---|
Access-Control-Allow-Origin |
Indicates whether a resource can be shared based by returning the value of: the Origin request header, *, or null. Even though * is allowed by the CORS spec, don't use it because this implies that every domain is acceptable. Use the value of the Origin request header instead. |
myoriginatingdomain.com |
Access-Control-Allow-Credentials |
Indicates whether the response to a request can be exposed when the omit credentials flag is unset. When part of the response to a preflight request it indicates that the actual request can include user credentials. | true |
Access-Control-Allow-Headers |
Indicates which HTTP headers are safe to expose to the API of a CORS API specification. | Authorization |
Access-Control-Allow-Methods |
Indicates, as part of the response to a pre-flight request, which HTTP Methods can be used during the actual request. | GET, POST, PUT, DELETE |
CORS Implementations
There are a couple of good implementations:
To have common/reusable CORS functionality in a single place across our APIs (i.e., the DRY Principle), we should consider:
- Populating the
OriginRequest Header from Mobile app and web applications.
Security References
- See "Securing Services", "Authentication", "Authorization", "Transport Security", and "Application Security" sections in the Pearson eCollege RESTful Services Best Practices Guide. Also see the "Handling Cross-Domain Issues" section.
- CORS Specification
- Enable CORS
- See "Always use HTTPS" and "CORS" sections in 18F API Standards.
Responses
When a Consumer invokes an API, one of three things can happen:
- Success - everything worked properly.
- Consumer Error - The Consumer (i.e., client) used bad data or there was a malformed request.
- API Error - The API (i.e., Producer, server) had a processing error.
Each API needs to handle errors and convey an appropriate status and error message in the HTTP Response.
HTTP Status Codes
Here is a list of the most common HTTP Status Codes, their usage, and contextual meaning.
| HTTP Status Code | Applicable HTTP Verb / Method | Meaning |
|---|---|---|
| 200 | GET / PUT / DELETE | OK. The API processed the request properly without error. |
| 201 | POST | Created. API successfully created the Resource. The URI of the newly created Resource should go in the Location Header of the Response. |
| 202 | POST / PUT / DELETE | Accepted. The Request has been been received, has not been completed, and will be processed later. |
| 204 | POST / PUT / DELETE | No Content. API successfully processed the request, and is not returning data. |
| 301 | GET / PUT / DELETE | Moved Permanently. This and all future requests should be directed to the new URI given in the Response. |
| 304 | GET | Not Modified. The resource hasn't changed since the previous GET request. Consumer must provide the following Headers: Date, Content-location, ETag. |
| 400 | ALL | Bad Request. Due to Malformed URI or invalid data in the request parameters or body. |
| 401 | ALL | Unauthorized. Indicates that the Consumer has not provided appropriate credentials or that the supplied credentials are invalid. |
| 403 | ALL | Forbidden. Similar to 401 (Unauthorized), but the API chose not to respond. This could be due to the fact that the HTTP Verb for the Request URI is not allowed. |
| 404 | GET / PUT / DELETE | Not Found. The API couldn't find the resource specified by the Request URI. |
| 409 | PUT | Conflict. The Request can't be processed due to an edit conflict on a resource. This could be caused by a race condition on the data to multiple updates from different consumers on the same resource. |
| 410 | ALL | Gone. The resource is unavailable and will not be available again. |
| 500 | ALL | Internal Server Error. The request couldn't be processed due to a server-side processing exception. |
| 501 | ALL | Not Implemented. The request is valid, but functionality has not been implemented for the HTTP Verb and Request URI. |
| 503 | ALL | Service Unavailable. The API (i.e., server) is temporarily unavailable. The server could be overloaded or down for maintenance. |
Error Handling / Messages
Error Handling / Messaging requirements are different for Internal APIs and Consumer-Facing APIs.
Internal API Error Handling
HTTP Status Codes are sufficient for expressing statuses and error conditions for APIs that are consumed by other APIs. Internal APIs could handle errors (especially uncaught exceptions) and return the information in the JSON response as follows:
{
"message": "Error message high-level description.",
"exception": "[detailed stacktrace or error message]"
}
In production, we shouldn't have to put error messages in the response.
Consumer-Facing API Error Handling
Consumer-facing applications (both web and mobile) - that consume an API and display error information to the end user - need more contextualized information is needed for consumer-facing applications. In this case, errors that include highly contextualized (localized, relevant to the action they're taking, etc) error messages would provide a much better experience to the user when there is an error.
Based on the JSON API Error Formatting Guidelines, APIs used by consumer-facing applications could provide the following fields in the error response:
- Required:
code- An application-specific error code, expressed as a string value.
- Optional:
title- A short, human-readable summary of the problem. It should not change from occurrence to occurrence of the problem. This would be non-localized (i.e., English) because developers are the audience this.details- A human-readable explanation specific to this occurrence of the problem. This would also be non-localized (i.e., English).user_message- Localized error message, to be conveyed to the (mobile) end user.
Here's an example error message:
{
"errors": [
{
"code": "unrecoverable_error",
"title": "The flux capacitor disintegrated",
"details": "Hold on, the end is nigh.",
"user_message": {
"default": "OMG, panic!",
"en-GB": "Keep a stiff upper lip",
"de-DE": "Schnell, schnell!!"
}
}
]
}
Response Validations
The ls-api_validation gem automates response validations. The gem looks at either your Swagger documentation or published JSON Schema documents to ensure your responses live up to their promises. In development ls-api_validation will raise an exception if the schema is invalid and in production it will validate a percentage of responses (1% by default) and log statuses. These actions are also configurable so you can set these to match your use cases.
Error Handling References
Here are some error handling examples from other API Design Guides:
- "Generate structured errors" from the Heroku API Guide.
- "Error handling" in the 18F API Standards.
- JSON API Error Formatting Guidelines.
Response Envelopes and Hypermedia
Core RESTful principles are great at describing which HTTP verb to use and how to design manageable URIs. But there are two problems that need to addressed in the Response:
- Links between API endpoints/resources.
- Standardized response structures.
HAL Specification
Of the several Hypermedia specifications we reviewed (see Hypermedia Specifications Considered), we prefer HAL because:
- It's simple and lightweight.
- The links (
_links) and data (_embedded) don't impose heavy structural or formatting changes to the response data. The JSON response data still refelects the intent/structure of the original resource. - Much of the specification is optional, so it's easy to comply with its requirements.
Here's an example:
GET /orders/523 HTTP/1.1
Host: example.org
Accept: application/hal+json
HTTP/1.1 200 OK
Content-Type: application/hal+json
{
"_links": {
"self": { "href": "/orders" },
"next": { "href": "/orders?page=2" },
"find": { "href": "/orders{?id}", "templated": true }
},
"_embedded": {
"orders": [
{
"_links": {
"self": { "href": "/orders/123" },
"basket": { "href": "/baskets/98712" },
"customer": { "href": "/customers/7809" }
},
"total": 30,
"currency": "USD",
"status": "shipped"
},
{
"_links": {
"self": { "href": "/orders/124" },
"basket": { "href": "/baskets/97213" },
"customer": { "href": "/customers/12369" }
},
"total": 20,
"currency": "USD",
"status": "processing"
}
]
},
"currentlyProcessing": 14,
"shippedToday": 20
}
In the above document:
- The
_linkssection at the root contains links for pagination (self,next) and search (find). - The
_embeddedresources section contains:- An
ordersarray, whose elements each contain:- Order properties:
total,currencyandstatus - Links to associated resources:
basketandcustomer
- Order properties:
- An
Please note that the _links object is not required by each order object - it is optional in that context.
Here are the requirements for a simple valid HAL document:
- The Response Header must include the following MIME type:
application/hal+json - The root object must be a Resource Object, which is one of:
_links- Optional. Contains links to other resources. Thelinksobject has several optional properties. Here's the main property, which is required:href- Its value is either a URI or a URI Template.
_embedded- Optional. Contains embedded resources, which could be a full or partial version of
the representation served from the target URI.
HAL is simple and works well, and we should use it for every JSON response from our APIs. Here are a couple of example use cases for HAL:
- Pagination
- Reduce coupling and payload size for deep object hierarchies. For example, an
ordercould have links to eachlineItemthat it contains rather than embedding the full content for thelineItemobjects within theorder.
Developer Libraries for working with HAL
Here's a list of developer libraries for working with HAL. The most promising libraries are:
- Rails:
- Clojure:
- JavaScript
The above list is a small subset of the available HAL libraries, so individual development teams should research and try out several options to find what works best for them.
Hypermedia Specifications Considered
There are several Hypermedia specifications that enable developers to link API responses and standardize API response formats:
We reviewed the above options for a consistent envelope that standardizes:
- Data - The payload (the main data in the Response).
- Links - What can the consumer do next?
- Metadata - Documentation without requiring the consumer to read through the API docs.
In general, we found that all of the above met these needs, but there were issues with the Data and Metadata structures required by most of these specifications:
- Data - If this imposes too many structural and formatting changes to existing JSON Responses, then it's too heavy and obfuscates the meaning/structure of the resource data.
- Metadata - The embedded documentation required by these specifications seems too heavy to include in a JSON Response. The developer consuming the API can just read the API documentation.
Hypermedia References
- On choosing a hypermedia type for your API - HAL, JSON-LD, Collection+JSON, Siren, Oh My!
- Please, no more generic hypermedia types
- The Hypermedia Debate
- JSON-LD and Why I Hate the Semantic Web
- HAL specification
- HAL site
- HAL GH Repository
- HAL Primer
- MuleSoft:
- Hypermedia API on Rails
Response Meta Data
Occasionally there will be a need to return meta-data about a response that doesn't belong in the JSON for the individual objects in the response. This data should go in a hash in a top level meta attribute. For instance, catalog-service can return offers using one of many different 'representations'. To include the name of the representation used for this particular response we might return something like this:
GET /offers/{guid}.json
{
"meta": {
"representation": "tile",
},
"offer": {
"id": 123,
... other attrs ...
}
}
GET /offers.json?some=search_query
{
"meta": {
"representation": "tile",
},
"offers": [ ..list of offers... ],
...
}
JSON
Our APIs and Kafka messages use JSON because it is the format of choice for most modern Web APIs.
JSON provides interoperability and cleanly converts to programming constructs (i.e., objects, arrays, key/value pairs) in most development languages.
Data Formatting
Dates and Times
We use RFC 3339 to represent Dates and Times in UTC (Coordinated Universal Time) format for interoperability. Here's an example of an RFC 3339-compliant date/time:
2008-09-08T22:47:31Z-05:00
Here's an example of the date in a JSON document:
{
...
"created_at": "2008-09-08T22:47:31Z-05:00"
...
}
Here are the formatting guidelines:
Tseparates the date from the time.Zseparates the time from the UTC offset, which indicates the Time Zone.- Dates have the following format -
YYYY-MM-DD, where:YYYYis a 4-digit year.MMis a 2-digit month. Prefix with a0if the month # is < 10.DDis a 2-digit day of the month.
- Timestamps have the following format -
hh:mm:sss:sss, where:hh- 2-digit hour based on a 24-hour clock.mm- 2-digit minutes.ss- 2-digit seconds.sss- 3-digit fractional seconds.- UTC Offsets have the following format -
?[hh]:[mm], where:?indicates whether the offset is ahead (+) or behind (-) UTC (Coordinated Universal Time). For example,-05:00indicates US Eastern Standard Time (EST).hh- 2-digit hour based on a 24-hour clock.mm- 2-digit minutes.
The Relationship Between RFC 3339 and ISO 8601
RFC 3339 and ISO 8601 both provide a standard for representing dates and times using the Gregorian Calendar. Per Wikipedia and other sources:
- RFC 3339 is a profile of (i.e., derived from, based on) ISO 8601.
- RFC 3339 requires a complete representation of date and time (only fractional seconds are optional).
- RFC 3339 differs from ISO 8601 as follows:
- RFC 3339 deviates from ISO 8601 in allowing a zero timezone offset to be specified as "-00:00", which ISO 8601 forbids.
- RFC 3339 only allows a period character to be used as the decimal point for fractional seconds.
- RFC 3339 allows the "T" separator to be replaced by a space or another character, while ISO 8601 allows it to be omitted.
UTF-8
Each API should use UTF-8 when it returns a JSON response because:
- Section 3 (Encoding) of the JSON Specification (RFC 4627) states the default encoding is UTF-8.
- UTF-8 provides a greater degree of interoperability than UTF-16 or UTF-32.
Just add a charset notation in the HTTP Content-Type response Header:
Content-Type: application/json; charset=utf-8
Currency and Fixed-Point Values
Monetary (i.e., Currency) information (e.g., the price of an offer (option)) should follow the ISO 4217 - Currency Code standard:
- A 3 letter Alphabetic Currency Code (i.e., the
?currency_codebelow) that consists of:- The first 2 letters correspond to a country code as specified by the ISO 3166-1 alpha-2 country codes.
- Third letter corresponds to the first letter of the currency name (i.e., major currency unit) for that country.
- For example,
USDrepresents the US Dollar, andJPYrepresents the Japanese Yen.
- The amount/value of the major currency unit (see
valuebelow) as an integer. - The currency exponent (see
exponentbelow) assumes a base of 10. For example, USD (the United States Dollar) is equal to 100 of its minor currency unit (the "Cent"). So the USD has exponent 2 (10 to the power 2 is 100, which is the number of Cents in a Dollar). Theexponentis the number of digits after the decimal operator.
Example:
{
"price": [
{?currency_code": "USD", "value": 1000, "exponent": 2 },
{"currency_code": "JPY", "value": 200, "exponent": 0 }
]
}
In the above example:
- US Dollar amount is $10.00 USD, and the Japanese Yen amount is 200 JPY.
- Please note that the code
JPYhas theexponent0, because its minor unit, the Sen (although nominally valued at 100th of a Yen), is of such negligible value that it is no longer used.
Fixed-point values (e.g. discount percentage) should follow a similar format:
{
"tax_rate":
{"value": 875, "exponent": 2 }
}
In the above example:
- The tax rate is 8.75%.
Third Parties and XML
Sometimes third parties (e.g., San Diego Zoo, SeaWorld/Busch Gardens) use XML, and our applications would simply consume the XML and convert it to JSON. The use of XML should be limited to data exchange with the third parties that still use XML.
Consumer Tooling
Ruby Consumer Tools
Client gems are a bit of an anti-pattern that can lead to service logic
being duplicated client-side. Moving forward we'd like teams to rely on
libraries like ls-api_client and ls-api_model to integrate their
new services into applications. These libraries are fairly stable but
improvements are always welcome.
Artifacts
Each API should come with the following artifacts to help consumers use the API:
- Documentation
- Executable Examples
Documentation
Each API should have human-readable documentation so that consumers
know how to use the API.
All of our APIs should use Swagger to generate
documentation. Many of our APIs already use it successfully. With
Swagger, you use a JSON to specify an API's endpoints, and then
generate the documentation as HTML.
Our internal Swagger documentation lists all published swagger docs for
all implementing services.
Here are some resources for getting started with Swagger:
Executable Examples
- Each API should provide executable examples that users can test directly from their terminal (e.g.,
curl),
a browser plugin (e.g., Advanced REST Client Plugin in Chrome or Firefox), or client GUI (e.g., Paw). This reduces dependencies between applications and simplifies testing. - Each API (regardless of platform) should be deployed in the staging environment to support this effort.
- See also Response Validations for info on test and production validations.
Performance
Both the API Consumer (i.e., the Client) and Producer (i.e., the Server) play a role in improving API performance.
Consumer-Side
The fastest API is call is one that isn't made in the first place. The API Consumer should consider caching if it can live with data that is slightly out of date. Please see SOA Series Part 4: Caching Service Responses Client-Side for details on how to implement this technique.
Producer-Side
There are several ways to improve the performance of an API on the Producer/Server side:
- Reduce the Response Size
- Use Pagination to manage large result sets.
- Condense the JSON response with HTTP Compression.
- Make the API work less with a Conditional
GETwith Caching andETag.
Condense JSON Response with HTTP Compression
HTTP Compression improves data transfer speeds and reduces bandwidth usage. There are 2 common compression schemes:
We should consider leveraging GZip to compress JSON Responses. Many implementations have seen a 60-80% reduction in payload size. Here are a couple of implementation suggestions:
- Clojure - Use
java.util.zip.GZIPOutputStream - Rails - Use
Rack::Deflater. Please see Honey, I shrunk the internet! - Content Compression via Rack::Deflater for an example of how to useRack::Deflater.
Conditional GET with Caching and ETag
Caching improves scalability by reducing calls to retrieve requested data, either from databases or other services. Each API should include an ETag HTTP Header in all responses to identify the specific version of the returned resource. The following table describes the other HTTP Request Headers that work with the ETag header:
| HTTP Request Header | Description | Example |
|---|---|---|
Cache-Control |
The maximum number of seconds (max age) a response can be cached. However, if caching is not supported for the response, then no-cache is the value. |
Cache-Control: 360 or Cache-Control: no-cache |
Date |
The date and time that the message was sent (in RFC 1123 format). | Date: Sun, 06 Nov 1994 08:49:37 GMT |
Pragma |
When Cache-Control is set to no-cache, this header is also set to no-cache. Otherwise, it is not present. |
Pragma: no-cache |
The following table describes the related HTTP Response Headers:
| HTTP Response Header | Description | Example |
|---|---|---|
Cache-Control |
The maximum number of seconds (max age) a response can be cached. However, if caching is not supported for the response, then no-cache is the value. |
Cache-Control: 360 Cache-Control: no-cache |
Date |
The date and time that the message was sent (in RFC 1123 format). | Date: Sun, 06 Nov 1994 08:49:37 GMT |
ETag |
Useful for validating the freshness of cached representations, as well as helping with conditional read and update operations (GET and PUT, respectively). Its value is an arbitrary string for the version of a representation, often a Hash that represents the value of the underlying domain object. |
ETag: "686897696a7c876b7e" |
Expires |
If max age is given, contains the timestamp (in RFC 1123 format) for when the response expires, which is the value of Date (e.g. now) plus max age. If caching is not supported for the response, this header is not present. | Expires: Sun, 06 Nov 1994 08:49:37 GMT |
Last-Modified |
The timestamp that the resource itself was modified last (in RFC 1123 format). | Last-Modified: Sun, 06 Nov 1994 08:49:37 GMT |
Here's an example set of HTTP Response Headers in response to a GET request on a resource that enables caching for one day (24 hours):
Cache-Control: 86400
Date: Wed, 29 Feb 2012 23:01:10 GMT
Last-Modified: Mon, 28 Feb 2011 13:10:14 GMT
Expires: Thu, 01 Mar 2012 23:01:10 GMT
In the above example, the API would:
- Cache data for a resource for 24 hours. During that time:
- The API would pull data from a cache rather than pull a new copy of the data from its original source (either a database or another service).
- Check the
Cache-Controlheader vs theDateheader in each HTTP Request to determine if the cache has expired. - If the cache has expired:
- Get a fresh copy of the data from the original source.
- Hash the data and store it in the cache.
- Generate a new
ETagheader and include it in the response.
Rather than duplicating the Cache-Control header vs Date header in each API, factor it out to a common place:
- In Rails, add a
before_filterinapp/controllers/application_controller.rbfor your project. - In Clojure, add a Ring filter that runs before the impacted routes in the API.
Performance References
- SOA Series Part 4: Caching Service Responses Client-Side
- SOA Series Part 6: Optimizing Your Service for Client Performance
- Support Caching with ETags - Heroku API Design Guide
- See the "Caching and Scalability" and "The ETag Header" sections from the Pearson eCollege RESTful Service Best Practices Guide
- Facebook - Using ETags
- Google Developer - Blogger JSON API: Performance Tips
- Investigate adding 'Expires', 'Cache-Control' and / or ETag headers to CMS API responses
Testing
Here's what most developers test for:
- Make sure that fields haven't changed.
- Check expected HTTP statuses.
- Check canned responses (example offers for catalog service).
Ruby Test Tooling
Here's a typical Rails-based API test environment:
- BDD/TDD with RSpec, MiniTest, etc.
- CodeClimate for Test Coverage.
- VCR to record and replay API calls so that test suites run faster. See How to Use VCR for more information.
- See also Response Validations
Mobile Test Tooling
The Mobile teams do something similar to VCR. They created VCRURLConnection, an iOS and OSX API to record and replay HTTP interactions. Any API to be consumed by a mobile app should use VCRURLConnection as part of their test environment.
In the future, the Mobile teams would like to have something similar to Mock Mode in their test suite.
Improve API Test Performance
A test can pull from 3 data sources when exercising an API:
- Invoke the actual service
- Pull from Cache
- Mock Mode
Consider the following techniques to improve the performance of API testing:
- Mock Mode:
- The gem sends back mock objects rather than invoking the service.
- Faster tests.
- Reduces dependencies when testing.
- Caching:
- The gem pulls from cache rather than call the service.
- Faster tests.
- Set from config file.
API Developer Guides - Implementing the API Design Guide on our Platforms
This document only covers what an API should look like, but not how to implement it. To see best practices for implementing APIs that fit with this Design Guide, please refer (and contribute) to the following pages:
- Ruby on Rails API Developer Guide (not started yet)
- Clojure/Play API Developer Guide (not started yet)
Why Do We Have Separate API Developer Guides?
The Developer Guides are separate from the API Design Guide to group issues/concerns at the right level. The goal here is to maintain a clear focus in each document. Furthermore, we have 2 development platforms at LivingSocial, each of which has its own set of unique challenges and implementation concerns.
Other API Design Guides
In addition to our experience, we've drawn on the the following external guides to help in the development of this API Design Guide:
- HTTP Specification (IETF)
- HTTP API Design Guide (Heroku)
- Pearson eCollege RESTful Service Best Practices Guide
- API Evangelist
- 18F API Standards - 18F
- Apigee
- A Practical Approach to API Design (Leanpub)
- Best Practices for Designing a Pragmatic RESTful API
- Dr. Roy Fielding's REST Dissertation
- Postel's Robustness Principle
JSON References
* feat: Notifications Page * update the unit tests * fix merge conflict * update the name * fix unit test * Test skill (#2) * Update en-US.json * bring in stuff from the draft branch * make deletion work * add error/warning icons * read notification map for state * fix type-checking and start on unit tests * add sampleDialog and fix more tests * add showAll * rename to onAllSelected because it's a callback * update unit tests * fix onSelect handling in ProjectTree * Update qna.test.tsx * Update design.test.tsx * add unit test * fixes from PR comments * restore multibots to tree * fix links from project tree * fix wrong IDs in URL * yarn.lock rebuild * update yarn.lock files * fix action card links * Update DesignPage.tsx * fix unit test * add unit test to ExpandableNode * update ProjectTree unit tests * Update ExpandableNode.tsx * add rootProjectId to triggerApi * fix creation and deletion bugs * change mocks folder name * Load design page only after skill project is loaded Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com> * fixes from CR * fix more tests * fix: handle the case when selection is not empty (microsoft#4561) Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com> * delete fd (microsoft#4556) Co-authored-by: Soroush <sorgh@microsoft.com> * fix more unit tests * revert: bf-orchestrator lib integration (microsoft#4568) This reverts commit 006a589. Co-authored-by: Andy Brown <asbrown002@gmail.com> * fix test typol * ci: remove code scanning workflow (microsoft#4569) * fix: Can not add properties in "SetProperties" action (microsoft#4572) * bug fix * pr comment * pr comments Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com> Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com> Co-authored-by: Srinaath Ravichandran <srravich@microsoft.com> Co-authored-by: zeye <zeye@microsoft.com> Co-authored-by: Soroush <hatpick@gmail.com> Co-authored-by: Soroush <sorgh@microsoft.com> Co-authored-by: taicchoumsft <61705609+taicchoumsft@users.noreply.github.com> Co-authored-by: Andy Brown <asbrown002@gmail.com> Co-authored-by: LouisEugeneMSFT <66701106+LouisEugeneMSFT@users.noreply.github.com> * update url * Revert "Test skill (#2)" This reverts commit c401768. * fix conflict * fix e2e test * fix unit test * update the diagnostic page * update the warning iconm * fix lint * add some unit tests * fix conflict and update the navigation * fix unit test Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com> Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com> Co-authored-by: Srinaath Ravichandran <srravich@microsoft.com> Co-authored-by: zeye <zeye@microsoft.com> Co-authored-by: Soroush <hatpick@gmail.com> Co-authored-by: Soroush <sorgh@microsoft.com> Co-authored-by: taicchoumsft <61705609+taicchoumsft@users.noreply.github.com> Co-authored-by: Andy Brown <asbrown002@gmail.com> Co-authored-by: LouisEugeneMSFT <66701106+LouisEugeneMSFT@users.noreply.github.com>
* extract visual editor from design page * bump shared packages * add botframework-ui dependencies * extract property editor from design page * don't pass rootPath as a prop * [wip] use adaptive form package * don't hoist botframework-ui packages * start stubbing out sync based new shell api * bump prompts plugin * revert to using shell extension container to render new form * add extension and adaptive form packages * update jest config to include display name * import json and prompts plugins * convert code editor to @monaco-editor package * render string field for expressions * remove height transition when there is an error * feat: Added select dialog ui plugin (#1) * feat: Added select dialog ui plugin * Apply suggestions from code review * Added tests * Clean up * Updated tests * renamed directory to select-dialog Co-authored-by: Andy Brown <asbrown002@gmail.com> * enable json ui plugin * allow customization of $role and $kind fields * [wip] begin lg editor field * fix height calculation * fix: Set default value in StringField to an empty string (#2) * use field name for lg fields * ignore lg lib * refactor plugin config add roleSchema and kindSchema * clean up lg plugin dependencies * drop ui prefix for ui options * bump @types/react and @types/react-dom * initial support of recognizers in plugin config * initial lu ui plugin * fix: Fixed object array border conflict (#3) * fix: Fixed object array border conflict * Modified getUISchema function * Resolve build errors * Fixed uiOptions in SchemaField * Reverted changes to getUISchema * [wip] start work on rendering better descriptions and help links * finish rendering description and help links * re-renable custom keybinding * clean up old bfcomposer monaco editor bits * fix build errors * do not enable minimap by default * feat: Add Select Dialog Plugin * Updated defaultUiSchema * Added select dialog plugin * fixed issue in uiSchema * Fixed luIntentOptions * Added help link * Modified tests * Fixed tests * move shared types from indexers to shared * fix merge issue * fix lint error * do not compile with test files * move prompt settings to 'Other' tab * fix: Moved prompt settings to 'Other' tab * fix: prompt settings styling and description * wrap labels in formatMessage Co-authored-by: Andy Brown <asbrown002@gmail.com> * move globally hidden fields to getOrderedProperties * [wip] add ability to have multi field rows * make sure to pass value to form rows * use json schema version 7 for base typings * add one of field * begin work on expression editor * update prompt plugin to use json schema v 7 * feat: Updated FormRow to support multiple fields in a row * feat: Updated FormRow to support multiple fields in a row * Updated FormRow * Updated FormRow * fix problem with yarn install * bump react versions * fix merge conflicts * correctly hide placeholder in code editor * remove inline recognizer editor * remove select intent plugin * add key to editor to force remounting * handle type and oneOf expression schemas * port inline luis intent editor * memoize initial value * rename lu plugin to luis * port regex intent editor * fix: Moved type selector to be inline with label (#7) * fix: Fixed warning related to link navigation styling (#8) * rename handleChange to handleRecognizerChange * add method to recognizer config to determine if selected * refactor code editors to not have dependency on monaco-editor package directly * downgrade monaco-languageclient There is an error initializing the language services * export OnInit type * clean up monaco dependencies * update sample schema * render all other fields on single row sets up support for defaultValue exression editor * render json editor with selected schema for complex types * feat: Added custom array field for set properties * feat: Added stackArrayItems to UiOptions * fix: Fixed array item borders * add designerId to ShellData formContext.dialogId is actually data.$designer.id which is confusing when there is dialogId in the ShellData. * feat: Updated ObjectArrayField to display complex arrays in a stacked layout * fix: Filtered object array properties to reflect ui order * removed console.log * fix: Fixed ExpressionField flicker * feat: Added add button for stacked array items * add schema prop to json editor * updated defautUiSchema.ts * register schemas on a per-model basis * fix merge build issue * trigger intent field change when editor changes * fix: Added 'expression' to expression type dropdown. * add disabled item actions for new array item * reduce height of lg field * pass raw errors to schema field child * plumb errors through the prompt form * fix array item styles when an error is present * use label overrides from ConceptLabels in form title * pass project id to lg editor * fix: Added 'expression' to type selector * implement arrayitem wrapper for array fields * fix: Added helpLinks to defaultUiSchema * add useArrayItems hook for array fields * fix: Added 'expression' to type selector * fix: added 'expression' to type dropdown * fix: combined 'selectedSchema' and 'selectedKey' into 'selectedOption' * feat: Added inline lu to prompts * do not override descriptions for prompt fields * fix array item usage * support expression fields with object properties * add subtitle to uiSchema * add titles to choices and confirmChoices * do not use label overrides in form title * [wip] use schema field to render all input fields * update ui schema * correctly set selected option * fix: fixed array item styles * feat: added formatData to uiOptions * fix: Added formatData to ObjectArrayField * default to correct type in expression editor * start case the fallback labels * allow labels for objects with properties * combine choice input and confirm input components * handle enum expressions * feat: Removed formatData in favor of serializer * fix: Fixed serializer labeling * Updated jest.config.js * fix: fixed recognizer in prompts * fix: fixed getSelectedOption returning expression option for strings * fix unit tests * Removed select intent * Fixed luis handleRecognizerChange method * fix merge issue * fixed error message displaying next to field in an array * removed kindSchema comments * update error message when recognizer can't be determined * add testid to expression dropdown * fix todo spec * fix notifications page spec * remove obiformeditor package * fix new LGTM alerts * incorporate locale when setting lg file name * fix selecting correct lu file with locale * sync data with shell to support undo/redo * update oneOf field to handle array of types * do not use transparent border for one of fields * do not have transparent border for expression editor It looks weird with the prefix icon * fix: Fixed dropdownWidth in expressions * fix: Fixed inline array styling * fix: removed unused variable * fix: Fixed lint issue * get localize lu file for inline intent editor * fix: Fixed error rendering issue * fix: Fixed schema field styles Co-authored-by: TJ Durnford <tjdford@gmail.com>
Description
example:
Task Item
Please include a link to the related issue. Ex.
Closes #<issue #>Type of change
Please delete options that are not relevant.
Checklist
Screenshots
Please include screenshots or gifs if your PR include UX changes.