{"id":18459134,"url":"https://github.com/smartbear/fake-ap","last_synced_at":"2025-04-08T05:34:56.699Z","repository":{"id":36964148,"uuid":"324522616","full_name":"SmartBear/fake-ap","owner":"SmartBear","description":"A fake AP module to help develop and test Atlassian Connect applications.","archived":false,"fork":false,"pushed_at":"2025-04-04T06:12:23.000Z","size":4942,"stargazers_count":14,"open_issues_count":14,"forks_count":4,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-04T07:23:03.781Z","etag":null,"topics":["cucumber-for-jira","open-source"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SmartBear.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-12-26T09:32:06.000Z","updated_at":"2025-04-04T06:12:27.000Z","dependencies_parsed_at":"2023-09-27T12:27:22.374Z","dependency_job_id":"20e33050-3042-486a-b41d-4aac3c30b3b7","html_url":"https://github.com/SmartBear/fake-ap","commit_stats":{"total_commits":857,"total_committers":7,"mean_commits":"122.42857142857143","dds":0.2147024504084014,"last_synced_commit":"5a70b0fa45465c6122c192f30fbe1f4d1637f2ca"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartBear%2Ffake-ap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartBear%2Ffake-ap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartBear%2Ffake-ap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmartBear%2Ffake-ap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SmartBear","download_url":"https://codeload.github.com/SmartBear/fake-ap/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247785919,"owners_count":20995641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cucumber-for-jira","open-source"],"created_at":"2024-11-06T08:21:58.604Z","updated_at":"2025-04-08T05:34:51.671Z","avatar_url":"https://github.com/SmartBear.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fake AP\nA fake AP module to help develop and test Atlassian Connect applications.\n\n[![Version][version-badge]][package]\n[![CI][ci-badge]][ci]\n[![Dependencies][dependencies-badge]][dependencies]\n[![License][license-badge]][license]\n\n## Introduction\n\nAtlassian Connect apps often use the [Atlassian Connect JavaScript API](https://developer.atlassian.com/cloud/jira/software/about-the-javascript-api/), also called **AP**, to overcome the limitations due to the app existing in an iframe from an Atlassian page.\n\nAP is typically included by calling the following script:\n```html\n\u003cscript src=\"https://connect-cdn.atl-paas.net/all.js\"\u003e\u003c/script\u003e\n```\n\nHowever this script only work when in an iframe from an Atlassian page. It means that when developing or testing, it is not possible to directly call the page if it is using AP.\n\nThis package provides a way to make a fake AP that can be used instead of the real one. It includes the most commonly used features of AP, including:\n- Generating JWT tokens\n- Dialogs\n- Events\n- Flags\n- History\n- Request\n- User locale\n\n**Note: This package should never be used on a production environment.**\n\n## Installation\n\nUsing npm:\n```\nnpm install --save-dev @smartbear/fake-ap\n```\n\nUsing yarn:\n```\nyarn add -D @smartbear/fake-ap\n```\n\n## Usage\n\n### Create a fake AP\n\nSimply create a Fake AP instance an make it available globally:\n\n```javascript\nimport FakeAP from '@smartbear/fake-ap'\n\nwindow.AP = new FakeAP()\n```\n\n### Setup the dialogs and flags React components\n\nTo display modal dialogs and flags (using `AP.dialog.create` and `AP.flag.create`), Fake AP provides two React components that you should mount or render.\nThese components are React portals, which means you can safely insert them anywhere in your React component tree as they will be rendered in another element outside.\n\nFor instance, given the following HTML:\n\n```html\n\u003cbody\u003e\n  \u003cdiv id=\"root\" /\u003e\n\u003c/body\u003e\n```\n\nYou can mount a React component with Fake AP like this:\n\n```javascript\nimport React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { APDialogs, APFlags } from '@smartbear/fake-ap'\n\nconst root = ReactDOM.createRoot(document.getElementById('root'))\n\nroot.render(\n  \u003cdiv\u003e\n    \u003cAPDialogs /\u003e\n    \u003cAPFlags /\u003e\n    \u003cdiv\u003eSome content\u003c/div\u003e\n  \u003c/div\u003e\n)\n```\n\nThis will render the following document:\n\n```html\n\u003cbody\u003e\n  \u003cdiv id=\"root\"\u003e\n    Some content\n  \u003c/div\u003e\n  \u003cdiv id=\"ap_dialogs\" /\u003e\n  \u003cdiv id=\"ap_flags\" /\u003e\n\u003c/body\u003e\n```\n\nThe `ap_dialogs` and `ap_flags` element will contain the working Fake AP components.\n\n### Use the fake AP\n\nThe fake AP creation should be done in a script included instead of the real one.\nFor instance, in for the project fake AP was originally created for, the script is available as a pack which is included when a flag is set.\nHere is an example using Rails and Webpacker:\n\n```erb\n\u003c% if ENV['USE_FAKE_AP'] %\u003e\n  \u003c%= javascript_pack_tag 'fake_ap' %\u003e\n\u003c% else %\u003e\n  \u003cscript src=\"https://connect-cdn.atl-paas.net/all.js\"\u003e\u003c/script\u003e\n\u003c% end %\u003e\n```\n\n## Configuration\n\nWhile most features work with no configuration, some methods require configuration to tell our fake AP which values to deal with.\nConfiguration can be done when creating the fake AP, or at any time later using the special `AP.configure` method:\n\n```javascript\nconst AP = new FakeAP({\n  locale: 'en_US'\n})\n\nAP.configure({\n  locale: 'fr_FR'\n})\n```\n\nHere is a list of all available configuration (refer to their own section for details):\n\n| Configuration                | Default value       | Description                                                       |\n| ---------------------------- | ------------------- | ----------------------------------------------------------------- |\n| `clientKey`                  | `null`              | The client key for `AP.context.getToken`                          |\n| `sharedSecret`               | `null`              | The shared secret for `AP.context.getToken`                       |\n| `userId`                     | `null`              | The user ID for `AP.context.getToken`                             |\n| `context`                    | `null`              | The context for `AP.context.getToken` and `AP.context.getContext` |\n| `dialogUrls`                 | `{}`                | URLs to call when using `AP.dialog.create`                        |\n| `locale`                     | `en_US`             | The user locale for `AP.user.getLocale`                           |\n| `requestAdapter`             | `RequestAdapter`    | The request adapter for `AP.request`                              |\n| `notImplementedAction`       | `() =\u003e {}`          | The method called when using a method that is not implemented     |\n| `missingConfigurationAction` | `throw new Error()` | The method called when a configuration is missing                 |\n\n**Note:** when using `AP.configure`, all previous configuration is kept, only conflicting configuration is replaced. All new configuration is added.\n\n### `AP.context.getToken`\n\nTo use `AP.context.getToken`, which creates a valid JWT token as Atlassian would do, it is required to provide the tenant client key and shared secret, as well as a user ID:\n\n```javascript\nAP.configure({\n  clientKey: 'key',\n  sharedSecret: 'secret',\n  userId: 'user'\n})\n```\n\nYou can also configure a context that will be added to the payload (see `AP.context.getContext`). By default that context is an empty object.\n\n### `AP.context.getContext`\n\nTo use `AP.context.getContext`, you can provide a context object to the configuration:\n\n```javascript\nAP.configure({\n  context: {\n    jira: {\n      project: {\n        id: '10000'\n      }\n    }\n  }\n})\n```\n\nBy default the context is an empty object. The same context will also be added to the payload of `AP.context.getToken`.\n\n### `AP.dialog`\n\nTo use dialogs (`AP.dialog.create`), you need to provide the dialog keys and URLs as they are describe in the descriptor:\n\n```javascript\nAP.configure({\n  dialogUrls: {\n    'custom-dialog': 'https://localhost:3000/dialog'\n  }\n})\n\nAP.dialog.create({\n  key: 'custom-dialog'\n})\n```\n\n### `AP.user.getLocale`\n\nYou can specify the user locale (defaults to `en_US`):\n\n```javascript\nAP.configure({\n  locale: 'fr_FR'\n})\n```\n\n### `AP.request`\n\n`AP.request` is the most complex to implement. Because of same-origin policy, it is not possible to implement a native request method using a shared secret configuration. Since several solutions are possible to work around this limitation, **adapters** have been designed to provide a request method.\n\n#### Default adapter\n\nIf you do not specify any adapter, the default behavior when using `AP.request` is to do nothing.\nIt will just call the `notImplementedAction` method if provided:\n\n```javascript\nAP.configure({\n  notImplementedAction: console.log\n})\n\n// This will call console.log('AP.request', 'path', { method: 'POST' })\nAP.request('path', { method: 'POST' })\n```\n\n#### Backend adapter\n\nThe backend adapter is a way to make actual requests to the Jira API using your own backend. It requires some work from the backend though:\n- Implement a Jira client to interact with the Jira API\n- Provide an API endpoint for the fake AP to call\n\nThis API endpoint should not require any authentication method, as the adapter does not provide any. **For obvious reasons, ensure that this API endpoint is never available on production.**\n\nBackend adapter should be configured like this:\n\n```javascript\nimport FakeAP, { BackendRequestAdapter } from '@smartbear/fake-ap'\n\nconst backendRequestAdapter = new BackendRequestAdapter('/path/to/fake_ap/api')\n\nconst AP = new FakeAP({\n  requestAdapter: backendRequestAdapter\n})\n```\n\nThen a POST request to the backend API will be made using the `AP.request` options.\nFor instance, consider following request:\n\n```javascript\nAP.request(\n  '/rest/api/3/search',\n  {\n    data: { some: 'data' }\n  }\n)\n```\n\nThis will make a POST request to your backend with a request body containing:\n- `method = GET`\n- `path = '/rest/api/3/search'`\n- `data = { some: 'data' }`\n\nTo make this adapter as generic as possible, there is no additional information sent to the backend. If you need to know which tenant is this request for, you need to specify it with the configuration URL. For instance:\n\n```javascript\nAP.configure({\n  requestAdapter: new BackendRequestAdapter('/path/to/fake_ap/api/tenants/2')\n})\n```\n\n#### Custom adapters\n\nIt is possible to create a custom adapter by extending from the default adapter:\n```javascript\nimport { RequestAdapter } from '@smartbear/fake-ap'\n\nclass CustomAdapter extends RequestAdapter {\n  async request() {\n    return {\n      body: '{}'\n    }\n  }\n}\n```\n\nHere are the specifications for a custom adapter:\n- Extend from `RequestAdapter`\n- Implement a `request` method that is async (or returns a promise)\n- If the request is a success, `request` should return an object with a `body` property that contained the JSON response as a string\n- If the request is a failure, `request` should throw the response body with a specific object:\n  ```javascript\n  // Assuming the response body was '{}' and status was 400\n  {\n    err: '{}',\n    xhr: {\n      responseText: '{}',\n      status: 400,\n      statusText: 'Bad Request'\n    }\n  }\n  ```\n\n### Not implemented method\n\nIt is possible to configure the behavior of AP methods that are not implemented in fake AP:\n\n```javascript\nAP.configure({\n  notImplementedAction: console.log\n})\n```\n\nWhen called, any method that is not implemented will call the `notImplementedAction` method with the method name and all the arguments.\nUsing the above configuration, calling `AP.context.getContext('a', 'b')` will call `console.log('AP.context.getContext', 'a', 'b')`.\nIt is possible to have more advanced behaviors:\n\n```javascript\n// This will forward all arguments to console.log, but will silence AP.resize calls\nAP.configure({\n  notImplementedAction: (method, ...args) =\u003e {\n    if (method !== 'AP.resize') {\n      console.log(method, ...args)\n    }\n  }\n})\n```\n\n### Missing configuration\n\nWhen a method requires some configuration that is not provided (for instance `AP.context.getToken`), Fake AP will throw an error by default. It is possible to change this behavior:\n\n```javascript\nAP.configure({\n  clientKey: 'key',\n  userId: 'user',\n  missingConfigurationAction: console.log\n})\n\n// This will call console.log('AP.context.getToken', 'sharedSecret', callback)\nconst callback = () =\u003e {}\nawait AP.context.getToken(callback)\n```\n\n## Implemented methods\n- `AP.context`:\n  - `getToken`\n  - `getContext`\n- `AP.dialog`:\n  - `create`\n  - `close`\n  - `getCustomData`\n- `AP.event`:\n  - `on`\n  - `once`\n  - `off`\n  - `emit`\n- `AP.flag.create`\n- `AP.history`:\n  - `getState`\n  - `popState`\n  - `pushState`\n  - `replaceState`\n- `AP.request`\n- `AP.user.getLocale`\n\n**Note:** `AP.dialog.create` does not handle every options. It handles the `key` option properly, but will show a dialog as if the options were:\n\n```javascript\n{\n  width: '100%',\n  height: '100%',\n  chrome: false\n}\n```\n\n## Not implemented methods\n\nFake AP is still missing a lot of methods from the actual AP:\n- `AP.cookie`\n- `AP.dialog`:\n  - `getButton`\n  - `disableCloseOnSubmit`\n  - `createButton`\n  - `isCloseOnEscape`\n- `AP.event`: all **any** and **public** events\n- `AP.history`:\n  - `back`\n  - `forward`\n  - `go`\n- `AP.host`\n- `AP.iframe`: `AP.resize` and `AP.sizeToParent`\n- `AP.inlineDialog`\n- `AP.jira`\n- `AP.navigator`\n- `AP.user`:\n  - `getCurrentUser`\n  - `getTimeZone`\n\nSome methods like `AP.resize` do not really make sense in a development or testing environment, so they may not be implemented before a long time.\n\n## Custom implementations\n\nOnce Fake AP is created, it is possible to add any custom implementation that is specific to your application.\n\nThe example below is a custom implementation of `AP.navigator.go`. It checks that you are on a specific page (`/normal_page`) and are trying to navigate to an issue. If it is the case it will check the correct issue ID using `AP.request`, then redirect to the correct page using that issue ID.\n\n```javascript\nAP.navigator.go = async (target, context) =\u003e {\n  if (target === 'issue' \u0026\u0026 window.location.pathname === '/normal_page') {\n    const response = await AP.request(`/rest/api/3/issue/${context.issueKey}`)\n    const issueId = JSON.parse(response.body).id\n\n    window.location = `/issue_page?issueId=${issueId}`\n  }\n}\n```\n\n[version-badge]: https://img.shields.io/npm/v/@smartbear/fake-ap\n[package]: https://www.npmjs.com/package/@smartbear/fake-ap\n[ci-badge]: https://img.shields.io/github/workflow/status/smartbear/fake-ap/CI?logo=github\n[ci]: https://github.com/SmartBear/fake-ap/actions?query=workflow%3ACI\n[dependencies-badge]: https://img.shields.io/david/smartbear/fake-ap\n[dependencies]: https://david-dm.org/smartbear/fake-ap\n[license-badge]: https://img.shields.io/npm/l/@smartbear/fake-ap\n[license]: https://github.com/SmartBear/fake-ap/blob/master/LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmartbear%2Ffake-ap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmartbear%2Ffake-ap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmartbear%2Ffake-ap/lists"}