Code Monkey home page Code Monkey logo

atlassian-connect-core's Introduction

Atlassian Connect Core

Latest Version on Packagist Software License Build Status Quality Score Total Downloads

The easiest way to create an add-on for JIRA and Confluence.

Version Compatibility

Laravel Package
5.5.x 1.2.x
6.x 1.3.x

Getting Started

Installing

Install dependency via Composer

$ composer require "brezzhnev/atlassian-connect-core"

Register route middleware jwt by adding to app\Http\Kernel.php the following line:

'jwt' => \AtlassianConnectCore\Http\Middleware\JWTAuth::class

Set the authentication driver to jwt in config/auth.php:

'guards' => [
    'web' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
...

Set the model class in config/auth.php providers section:

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => \AtlassianConnectCore\Models\Tenant::class,
    ],
...

Register the subscriber in the app/Providers/EventServiceProvider.php:

/**
 * The subscriber classes to register.
 *
 * @var array
 */
protected $subscribe = [
    \AtlassianConnectCore\Listeners\PluginEventSubscriber::class
];

Configure the database and run:

php artisan migrate
php artisan plugin:install

The command php artisan plugin:install will publish config, views and resources that you can change for your needs.

Also, it will create "dummy" tenant needed for local testing and developing without needing of installing the add-on on real JIRA or Confluence instances.

Publishing as an add-on

If your application returns the add-on descriptor on the request to URL http://localhost:8000/atlassian-connect.json it means you are close to happiness and you can install the add-on.

Step 1. Make your application accessible

To install the add-on in the instance, you should be visible through the internet. For testing purposes the easiest way is to use ngrok.

Then you have it accessible, put your actual website URL to environment variable PLUGIN_URL.

Make sure your add-on is accessible via HTTPS, it is a requirement.

Step 2. Configure your JIRA / Confluence instance

Configure your add-on using config/plugin.php. Most values may be overwritten using env vars.

Step 3. Upload your add-on

Finally, you need to upload the add-on. Click "Upload add-on" and paste your public URL with descriptor path, eg. https://d1ea31ce.ngrok.io/atlassian-connect.json or https://yourplugindomain.com/atlassian-connect.json

Step 4. Testing the successfulness

After the successful installation, on JIRA instance you may see "Your add-on" top menu item. You also can go to the add-on general page by direct link :product_base_url/plugins/servlet/ac/sample-plugin/hello-page

Instead of :product_base_url you should put your JIRA or Cofluence instance URL (eg. https://google-dev.atlassian.net).

If you see page working, the application configured and add-on installed correctly.

Publish resources

Instead of using plugin:install you can perform actions manually.

To copy all the publishes you should use the following command:

php artisan vendor:publish --provider="AtlassianConnectCore\ServiceProvider"

To copy only specific publish you must call this command with option --tag. The value can be public (to copy assets), views and config.

Usage

Default routes

The following routes are registered by default:

  • GET /atlassian-connect.json descriptor contents
  • POST /installed add-on installed callback
  • POST /uninstalled add-on uninstalled callback
  • POST /enabled add-on enabled callback
  • POST /disabled add-on disabled callback
  • GET /hello sample page to persuade all working correctly

You may disable them by setting the config value plugin.loadRoutes to false.

Descriptor

You can use Descriptor facade to customize or create from scratch your own descriptor contents.

For example, you may customize it by adding to the app\Providers\AppServiceProvider in boot section the following:

Descriptor::base() // base descriptor contents
    ->setScopes(['admin' , 'act_as_user'])
    ->withModules([
        'webhooks' => [[
            'event' => 'jira:issue_created',
            'url' => route('webhookHandlerRouteName')
        ]]
    ])
    ->set('version', $this->getLatestPluginVersion());

Warning: if you are using route helper in the AppServiceProvider you should have RouteServiceProvider defined above AppServiceProvider in your app.php config.

API requests

In most cases of add-on development for Atlassian Product you need to perform requests to the instance.

For this case you can use JWTClient. It uses GuzzleHttp as HTTP client.

If you want to have custom handling (middlewares etc.) you can pass client instance to the constructor.

Pagination

If you want to send a request to an endpoint with pagination you should use JWTClient::paginate method. In most cases you don't need to pass paginator instance to the JWTClient constructor because it will be instantiated automatically by resolving your Tenant product type (JIRA or Confluence), but you always can use the specific paginator.

There are two paginators supported by default:

  • JiraPaginator
  • ConfluencePaginator

You're always able to extend Paginator class and create your own.

Examples

Get a Confluence page content

use AtlassianConnectCore\Http\Clients\JWTClient;

/**
 * Retrieve a Confluence page content.
 *
 * @return array
 */
public function pageContent(int $id): array
{
    $client = new JWTClient($this->tenant); // or Auth::user() if you performing a request from the instance
    
    return $client->get('rest/api/content/' . $id, [
        'query' => [
            'expand' => 'body.storage'
        ]
    ]);
}

Get a JIRA issue

use AtlassianConnectCore\Http\Clients\JWTClient;

/**
 * Retrieve an issue object.
 *
 * @return array
 */
public function viewIssue(string $key): array
{
    $client = new JWTClient($this->tenant);
    
    return $client->get('rest/api/2/issue/' . $key);
}

Webhooks

The plugin provides a convenient way to handle incoming webhooks, based on habitual Laravel Events.

If you don't familiar with Laravel Events, please take a look at Laravel Docs

There are two ways to define webhook listeners:

1. Define listeners in the config/plugin.php

'webhooks' => [
    'jira:issue_updated' => \App\Listeners\Webhooks\Issue\Created::class,
    ...
]

2. Define listeners using the Webhook facade, for example:

Webhook::listen('jira:issue_created', function(\AtlassianConnectCore\Models\Tenant $tenant, \Illuminate\Http\Request $request) {
    // ...
});

As you can see, you can define event listener as a closure or as a string in Laravel-like syntax:

Webhook::listen('jira:issue_created', \App\Listeners\Webhooks\Issue\Created::class);
Webhook::listen('jira:issue_created', 'App\Listeners\Webhooks\Issue\Created@handle');

You don't need to define the webhooks within your add-on descriptor, they will be described automatically.

Example listener

<?php

namespace App\Listeners\Webhooks\Issue;

use Illuminate\Http\Request;
use AtlassianConnectCore\Models\Tenant;

class Created
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the incoming webhook.
     *
     * @param \AtlassianConnectCore\Models\Tenant $tenant
     * @param \Illuminate\Http\Request $request
     *
     * @return void
     */
    public function handle(Tenant $tenant, Request $request)
    {
        // ...
    }
}

Your event listeners may also type-hint any dependencies they need on their constructors. All event listeners are resolved via the Laravel service container, so dependencies will be injected automatically.

Console commands

  • plugin:install is a helper command that creates "dummy" tenant with the fake data and publishes package resources (config, views, assets)
  • plugin:dummy provides interactive way to set a tenant as "dummy" without manually editing database

Tests

Run the following in the package folder:

vendor/bin/phpunit

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

atlassian-connect-core's People

Contributors

alexeykostenko avatar breart avatar dependabot[bot] avatar jvanraaij avatar mansilladev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

atlassian-connect-core's Issues

How to define and handle webhooks

Hello,

I need to listen when the user creates and modify a page in Confluence. Too I
would like to know if with your package a can do request to the API of
Confluence.

In your page of the package only I can see one example but I don't understand.

Can you help me send me some examples about there?

Thanks

Webhook example shows params in reverse

Param is handled as Tenant / Request whereas the example in the readme shows Request / Tenant

/**
* Handle the incoming webhook
*
* @param \Illuminate\Http\Request $request
* @param \AtlassianConnectCore\Models\Tenant $tenant
*
* @return void
*/
public function handle(Request $request, Tenant $tenant)
{
// ...
}

How to catch / emit an event?

Hello,

Wondering how an event should / can be emitted from the Controller. $client->getEmitter()? Thanks!

Regards,
Jessie

Send JWTClient requests with a custom token

I have an angular app which is communicating with laravel.
On every call I send JWT and it passes middleware with auth.
On Laravel there is a tenant database with data from installed instance.
Is there a way that I can create new JWTClient($jwt) to current user.
Becouse JWTClient(Auth::user()) gives me login from tenant database.

Unauthorized (401)

Hi,
have trouble, with secure request.
I am using
$client = new JWTClient(Auth::user());
dump($client->post('/rest/api/2/search'.urlencode('?jql=updated > 2014-01-20 and project in (PC) and timespent > 0&fields=summary,worklog&maxResults=1000')));
I got 401, can you please help me with it? Interesting, what
dump($client->get('/rest/api/2/issue/TEST-13'));
works perfectly, problem in jira permissions?

Unable to impersonate user

First up, thank you for sharing your work! I am trying to impersonate a user for a put request but can't seem to locate functions that might do that in the package (yet).

Reference: "Atlassian Connect for Express.js provides the .asUserByAccountId method to make requests on users' behalf." from https://developer.atlassian.com/cloud/jira/platform/user-impersonation-for-connect-apps. Example from ACE:
var httpClient = addon.httpClient(req);
httpClient.asUserByAccountId('accountid-of-user-to-act-as').get('/rest/api/latest/myself', function(err, res, body) {
...
});

Looking forward to hear from you soon. Thank you!

Regards,
Jessie

1.2.3 version get methods geting errors

For the usage with jira there are a lot of GEt methods that are not using query parameters.
So the calls are being normally without path?anything
And if we use those calls normally to get results the new JWTHelper have a error in the qsh function
PART:
// Parse a query into the map of parameters
parse_str($parts['query'], $params);

It is trying to get query parts but there isnt any query parts.

Change:
$params=array();
if(isset($parts['query']))
parse_str($parts['query'], $params);

Could you make change here because a lot of projects are using this so I dont need to update everyone manually

Does not support Laravel 6

Hello!

I tried to install this package on laravel 6 today and got rejected.

Could you please fix?

Thanks in advance,

Alena
image

Unauthorized exception on GET request (due to space in query parameter value)

Hi Brezzhnev.
I am trying to to create request to jira api to have users in group provided by name.

$res = $client->get('/rest/api/2/group?groupname=administrators&expand=users');

And this is working normaly.
But when I am trying to get users that are in group that has space in name like one I created.
"od vojeno" it is giving me the 401 resposne.
I have tried all those ways.

$res = $client->get('/rest/api/2/group?groupname=od vojeno&expand=users');

$res = $client->get('/rest/api/2/group?groupname=od+vojeno&expand=users');

$res = $client->get('/rest/api/2/group?groupname=od%20vojeno&expand=users');

$res= $client->get('/rest/api/2/group?groupname=od%20vojeno&expand=users');

$client->get('/rest/api/2/group',[
'query' => [
'groupname' => 'od vojeno',
],
]);
And all combinations with decoded/encoded parameter but cant make it run allways is 401 error

Broke when clientKey suddenly came with "jira:" prefix

Hi, everything seem to be working well until today when these messages were logged:

[2020-11-05 01:45:08] production.ERROR: SQLSTATE[HY000]: General error: 1835 Malformed communication packet (SQL: select * from tenant where client_key = jira:client-key-here and tenant.deleted_at is null limit 1) {"exception":"[object] (Illuminate\Database\QueryException(code: HY000): SQLSTATE[HY000]: General error: 1835 Malformed communication packet (SQL: select * from tenant where client_key = jira:client-key-here and tenant.deleted_at is null limit 1) at /.../vendor/laravel/framework/src/Illuminate/Database/Connection.php:669)
[stacktrace]
#0 /.../vendor/laravel/framework/src/Illuminate/Database/Connection.php(629): Illuminate\Database\Connection->runQueryCallback('select * from ...', Array, Object(Closure)) #1 /.../vendor/laravel/framework/src/Illuminate/Database/Connection.php(338): Illuminate\\Database\\Connection->run('select * from ...', Array, Object(Closure))
#2 /.../vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2149): Illuminate\Database\Connection->select('select * from `...', Array, true)
#3 /.../vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2137): Illuminate\Database\Query\Builder->runSelect()
#4 /.../vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2609): Illuminate\Database\Query\Builder->Illuminate\Database\Query\{closure}()
#5 /.../vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(2138): Illuminate\Database\Query\Builder->onceWithColumns(Array, Object(Closure))
#6 /.../vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(545): Illuminate\Database\Query\Builder->get(Array)
#7 /.../vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php(529): Illuminate\Database\Eloquent\Builder->getModels(Array)
#8 /.../vendor/laravel/framework/src/Illuminate/Database/Concerns/BuildsQueries.php(143): Illuminate\Database\Eloquent\Builder->get(Array)
#9 /.../vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php(131): Illuminate\Database\Eloquent\Builder->first()
#10 /.../vendor/brezzhnev/atlassian-connect-core/src/JWTGuard.php(92): Illuminate\Auth\EloquentUserProvider->retrieveByCredentials(Array)
#11 /.../vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php(307): AtlassianConnectCore\JWTGuard->user()
#12 /.../vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php(261): Illuminate\Auth\AuthManager->__call('user', Array)
#13 /.../app/Http/Controllers/DashboardController.php(16): Illuminate\Support\Facades\Facade::__callStatic('user', Array)
#14 [internal function]: App\Http\Controllers\DashboardController->__construct()

Looks like the call needs to be handled as a string i.e. clientKey value to be wrapped around quotes before sending to the database.

Using JWT token auth but also keep the driver session

Hello,

First time integrating atlassian connect core testing the jira server service desk. If I already have setup a Laravel auth where users use the driver session guard. How would I implement this package but still keep:

    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

Is there anywhere in the configuration i could change the web array to like:

    'web_atlassian' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],

Hope i made myself clear.

Best regards

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.