---
title: "Cum construim și livrăm aplicații Atlassian Forge"
description: "Am construit 25 de aplicații pentru Jira și Confluence pe câteva șasiuri comune și le-am trimis la review deodată. Patru sunt publice; celelalte 21 au fost respinse și stau blocate într-o stare de listare pe care doar Atlassian o poate curăța, fiindcă am sărit pasul care ne-ar fi arătat zidul: să ducem întâi o singură aplicație până la aprobare."
date: 2026-10-11
lang: ro
canonical: https://goingstellar.ro/ro/metode/cum-construim-si-livram-aplicatii-atlassian-forge/
author: Going Stellar
publisher: GOING STELLAR S.R.L.
topic: Aplicații / Atlassian Forge
license: CC BY 4.0
alternate_en: https://goingstellar.ro/methods/how-we-build-and-ship-atlassian-forge-apps/
---

# Cum construim și livrăm aplicații Atlassian Forge

*Am construit 25 de aplicații pentru Jira și Confluence pe câteva șasiuri comune și le-am trimis la review deodată. Patru sunt publice; celelalte 21 au fost respinse și stau blocate într-o stare de listare pe care doar Atlassian o poate curăța, fiindcă am sărit pasul care ne-ar fi arătat zidul: să ducem întâi o singură aplicație până la aprobare.*

> Aplicații · Atlassian Forge · 2026-10-11 · https://goingstellar.ro/ro/metode/cum-construim-si-livram-aplicatii-atlassian-forge/

## Pe scurt

- Înainte să trimiți o flotă de aplicații Atlassian Forge, du o singură aplicație prin review până la aprobare: un pilot arată blocajul o dată, în loc să-l multiplice pe fiecare listare.
- A fi listat în Atlassian Marketplace nu înseamnă a fi folosit: o listare aprobată pe care n-o caută nimeni e un raft, nu un public.
- Resolverul unei aplicații Forge trebuie să cheme api.asUser(), ca produsul să aplice permisiunile celui care face cererea, iar api.asApp() rămâne doar pentru declanșatoarele programate.
- Lipsa licenței în Forge nu înseamnă licență inactivă, deci poarta de plată are nevoie de trei stări (plătit, expirat și necunoscut), iar starea necunoscută nu blochează niciodată.
- Când un API acceptă ce formularul Marketplace refuză, nu e o scurtătură, e o datorie: pui întâi distribuția pe Sharing, apoi creezi listarea din interfață.

## Despre ce e vorba

Forge e platforma Atlassian pentru aplicații Jira și Confluence Cloud. Atlassian găzduiește codul, iar aplicația rulează în interiorul produsului. În august 2026 am construit 25 de aplicații Forge, 9 pentru Jira și 16 pentru Confluence, și le-am trimis pe toate în Atlassian Marketplace. Mai jos e metoda pe care o folosim acum, inclusiv părțile pe care le-am învățat greșind.

Pe 25 septembrie 2026 patru aplicații erau publice în Marketplace: Epic Roll-up, SprintEstimate, Templatize și Recurring Issues, fiecare verificată pe pagina ei publică. Celelalte 21 au fost respinse pe 8 septembrie și nu pot fi retrimise până când Atlassian nu curăță o stare de listare din sistemul lor; pe 29 septembrie echipa lor de suport a numit-o un caz-limită la care lucrează. Pe 21 septembrie datele publice nu arătau nicio instalare pentru vreuna dintre aplicațiile noastre. **A fi listat și a fi folosit sunt lucruri diferite, iar deocamdată putem arăta doar primul.**

## Un singur șasiu, multe aplicații

Nu proiectăm 25 de aplicații. Proiectăm un șasiu și îl umplem. O aplicație Jira înseamnă un manifest, un resolver (funcția de backend pe care o cheamă interfața) și un front-end în UI Kit. Referința e SprintEstimate, o tablă de planning poker, testată cap-coadă înainte să copiem ceva din ea. Macro-urile Confluence care cheamă un model de limbaj folosesc un al doilea șasiu, pe Custom UI; unsprezece astfel de macro-uri îl împart, iar faptul că verificarea de licență e identică la octet în toate unsprezece îl confirmăm cu un hash, nu citind.

Testăm aplicația publicată, nu codul. Conducem aplicația prin interfața ei reală și citim apoi efectul la sursă, prin API-ul REST al Jira: valoarea câmpului, numărul de issue-uri create, autorul unui comentariu. Așa am prins un buton Save care trimitea o valoare goală în locul mediei afișate și tot așa am văzut cum minte un test. Previzualizarea unui macro Confluence folosește aceleași clase CSS ca macro-ul salvat, deci un test care căuta acele clase trecea, deși niciun utilizator nu putea salva vreunul dintre cele 10 macro-uri livrate. Acum o salvare se dovedește prin închiderea dialogului de configurare și prin macro-ul randat fără câmpurile de editare.

## Poarta de licență, dovedită prin efect

O aplicație Forge plătită își citește licența din contextul cererii și oprește funcția plătită când licența lipsește. Ale noastre citeau un câmp care pe backend nu există. Obiectul din front-end îl numește `active`; obiectul primit de resolver îl numește `isActive`, iar framework-ul resolverului suprascrie ce a trimis front-end-ul. În toate cele 20 de aplicații monetizate, în 34 de locuri din cod, verificarea plății era deci mereu falsă: un client care plătea ar fi primit varianta gratuită. Reparația citește oricare dintre cele două nume.

Lipsa licenței nu înseamnă licență inactivă. Forge nu trimite niciun obiect de licență pentru edițiile gratuite și pentru instalările din afara producției, așa că a trata o licență lipsă drept neplătită îi blochează pe oameni care au tot dreptul să folosească aplicația. Folosim trei stări: plătit, expirat și necunoscut, iar starea necunoscută nu blochează niciodată.

Citirea codului n-a fost acceptată ca dovadă. `forge install` primește o valoare de licență, de exemplu active sau inactive, pe o instalare de development, deci drumul plătit se poate parcurge înainte de aprobare și fără nicio plată. Pe aplicația noastră de clonat issue-uri, cu licența inactivă s-a creat exact 1 issue, iar cu licența activă exact 3. Le-am numărat prin API, nu din mesajul de succes. Același test a găsit o aplicație la care limita exista doar în browser: backend-ul trimitea tot, iar front-end-ul ascundea surplusul, deci oricine deschidea consola din browser avea varianta plătită. Limita se aplică acum în resolver.

## Resolverul rulează ca utilizatorul

Atlassian scanează fiecare versiune trimisă cu un analizor static și deschide un tichet de securitate cu termen. Pe 31 august a respins două dintre aplicațiile noastre pentru o verificare de autorizare lipsă, cu severitate P2. Cauza era o clasă de bug-uri, nu două bug-uri. Fiecare apel către produs din resolver folosea `api.asApp()`, așa că oricine putea deschide panoul acționa cu permisiunile aplicației: o aplicație putea arăta istoricul de modificări al unui issue cuiva care nu avea voie să vadă issue-ul, alta putea crea issue-uri acolo unde utilizatorul nu avea dreptul să creeze.

Scannerul a găsit două; un grep pe toată flota a găsit cinci. Regula de acum: resolverul cheamă `api.asUser()`, ca produsul să aplice permisiunile celui care face cererea, iar `asApp()` e permis doar în declanșatoarele programate, unde nu există un utilizator. Funcția ajutătoare are implicit utilizatorul, deci un apel nou e sigur și dacă uiți să specifici ceva. Fiecare scriere trece în plus prin API-ul de autorizare Forge și, la eroare, refuză.

Am verificat reparația prin efect: în fiecare aplicație, issue-ul sau comentariul creat avea ca autor utilizatorul care a acționat, nu aplicația. Pe 1 septembrie, după retrimitere, scannerul Atlassian a rescanat ambele aplicații și a marcat tichetele ca reparate. N-a trimis niciun email când a făcut-o.

## Review-ul se face într-un tichet

Fiecare trimitere deschide un tichet în service desk-ul Atlassian, anunțat printr-un singur email. Întrebările recenzentului stau în tichetul acela: date de test pentru orice sistem terț cu care se integrează aplicația, motivul fiecărui scope de API și fiecare server extern către care aplicația trimite date. Dacă nu răspunde nimeni, review-ul expiră cu motivul *Timeout*, cuvânt pe care nu l-am găsit nicăieri în documentația Atlassian.

Am aflat asta târziu. Pe 27 august, 12 din 20 de listări stăteau respinse pe Timeout de peste două săptămâni. Mailurile veneau de pe un domeniu pe care nu-l căutam, iar clientul de mail le punea deoparte, în afara inboxului. Aplicațiile n-aveau nimic. Noi nu răspunseserăm.

Acum, la trimitere, notăm tichetul, verificăm că îl putem deschide și răspundem în aceeași zi la cele trei întrebări. Un watchdog rulează de două ori pe zi și citește starea din platformă, nu doar mailul, fiindcă un status se poate schimba fără niciun email.

## Capcana Sharing

Recenzentul trebuie să poată instala aplicația. O aplicație Forge care are licență în manifest sau a fost trimisă la listare nu mai poate fi partajată printr-un link de instalare, iar o listare nu se poate instala înainte de aprobare. Am construit cele 25 de copii fără licență prescrise de documentația Atlassian; recenzentul ne-a spus apoi că testarea se face doar pe aplicația trimisă.

Cauza reală am găsit-o după câteva săptămâni. Interfața Marketplace refuză să listeze o aplicație al cărei status de distribuție din consola de developer nu e *Sharing*. Noi creaserăm 21 de listări prin apelurile GraphQL ale consolei, unde verificarea asta nu există. Cele 4 aplicații puse pe Sharing înainte de listare au trecut prin review și sunt exact cele patru publice acum. Cele 21 au ajuns într-o stare pe care consola nu o poate reprezenta: controlul Sharing a rămas dezactivat și după respingere, iar scoaterea licenței nu l-a deblocat. Suportul Atlassian ne-a cerut să publicăm prin interfață, câte o aplicație, ca să ruleze toate validările.

**Când un API acceptă ce formularul refuză, nu e o scurtătură, e o datorie.** Pui întâi distribuția pe Sharing, apoi creezi listarea din interfață.

## Unde am greșit: pilotăm una înainte de flotă

Greșeala scumpă n-a fost un bug. Am trimis 25 de aplicații deodată, fără să ducem măcar una prin review până la aprobare. Un pilot ar fi lovit zidul Sharing la prima aplicație, în prima săptămână, cu 24 încă neconstruite. Noi am multiplicat de 25 de ori un blocaj.

O greșeală mai mică, de același fel: am raportat odată o reușită după ce am citit textul de ajutor al paginii (*Select the Sharing option, fill in the app details, and select Save changes*) ca și cum s-ar fi deschis un formular. Acum un control blocat îl dovedim scoțându-i atributul disabled, dând un click real și numărând dialogurile, câmpurile, cererile de rețea și lungimea textului din pagină. Pe aplicația blocată toate au rămas la zero, iar textul a fost identic la caracter; pe o copie fără licență, același click a deschis formularul. Contrastul e dovada.

Când am pilotat în sfârșit, pe o singură listare pe care Atlassian a scos-o ca s-o putem recrea, pilotul a scos la iveală încă un strat: cheia aplicației rămânea rezervată după arhivarea listării. L-am găsit pe o aplicație, nu pe 21.

## Limite

Metoda scade șansa unei respingeri sau a unei porți de plată stricate. Nu aduce clienți: o listare aprobată pe care n-o caută nimeni e un raft, nu un public. Testele de licență acoperă codul nostru, nu facturarea Atlassian, iar o cumpărare reală din Marketplace n-am văzut-o încă de la cap la coadă. Pașii de review descriși aici sunt cei întâlniți de noi în 2026.

## Lista de verificare

**1. Aplicația de referință.** Construiește una pe șasiu și test-o live, citind efectele la sursă, înainte s-o copiezi.

**2. Identitatea.** Niciun `asApp()` în afara declanșatoarelor programate; fiecare scriere verificată și refuzată la eroare.

**3. Licența.** Citește ambele nume de câmp, tratează licența lipsă drept necunoscută și testează prin efect instalări active și inactive.

**4. Întâi Sharing.** Pune distribuția pe Sharing, apoi creează listarea din interfață.

**5. Tichetul.** Notează-l la trimitere, răspunde în aceeași zi la cele trei întrebări ale recenzentului și urmărește starea din platformă, nu doar mailul.

**6. Pilotul.** O aplicație ajunge până la aprobare înainte ca restul să plece după ea.

## Întrebări și răspunsuri

### De ce a fost respinsă aplicația mea din Atlassian Marketplace cu motivul Timeout?

Un review din Atlassian Marketplace expiră cu motivul Timeout când nimeni nu răspunde recenzentului în tichetul de service desk deschis la trimitere. Întrebările recenzentului stau acolo: date de test pentru sistemele terțe, motivul fiecărui scope de API și fiecare server extern către care aplicația trimite date. Notează tichetul la trimitere, verifică faptul că îl poți deschide, răspunde în aceeași zi și urmărește starea din platformă, nu doar mailul.

### De ce nu poate recenzentul să instaleze aplicația mea Forge înainte de aprobare?

O aplicație Forge care are licență în manifest sau a fost trimisă la listare nu mai poate fi partajată printr-un link de instalare, iar o listare nu se instalează înainte de aprobare. Interfața Marketplace refuză în plus să listeze o aplicație al cărei status de distribuție nu e Sharing. Listările create prin apelurile GraphQL ale consolei ocolesc verificarea și pot rămâne blocate într-o stare pe care doar Atlassian o curăță.

### Cum verifică o aplicație Forge dacă un client are licență plătită?

O aplicație Forge citește licența din contextul cererii și trebuie să accepte ambele nume de câmp, fiindcă obiectul din front-end îl numește active, iar resolverul primește isActive. Licența lipsă înseamnă stare necunoscută, nu neplătită, pentru că Forge nu trimite licență la edițiile gratuite și la instalările din afara producției. Poarta se dovedește prin efect: instalări de development cu licență activă și inactivă, cu rezultatele numărate prin API.

### Ce trebuie să folosească resolverul Forge, asApp sau asUser?

Resolverul Forge trebuie să folosească api.asUser(), ca Jira sau Confluence să aplice permisiunile omului care face cererea. Cu api.asApp(), oricine deschide panoul acționează cu permisiunile aplicației și poate vedea istoricul unui issue interzis sau poate crea issue-uri fără drept. Funcția asApp() rămâne doar pentru declanșatoarele programate, funcția ajutătoare are implicit utilizatorul, iar fiecare scriere trece prin API-ul de autorizare Forge și refuză la eroare.

### Cum testezi corect o aplicație Atlassian Forge?

O aplicație Atlassian Forge se testează publicată, nu din cod: o conduci prin interfața ei reală și citești efectul la sursă, prin API-ul REST al Jira, de exemplu valoarea unui câmp, numărul de issue-uri create sau autorul unui comentariu. Un test care se uită doar la interfață poate minți. O salvare se dovedește prin închiderea dialogului de configurare și prin macro-ul randat fără câmpurile de editare.

## Cum se citează

**Citare:** Going Stellar (2026). *Cum construim și livrăm aplicații Atlassian Forge*. GOING STELLAR S.R.L.. https://goingstellar.ro/ro/metode/cum-construim-si-livram-aplicatii-atlassian-forge/

```bibtex
@misc{goingstellar2026how,
  title  = {Cum construim și livrăm aplicații Atlassian Forge},
  author = {{Going Stellar}},
  year   = {2026},
  url    = {https://goingstellar.ro/ro/metode/cum-construim-si-livram-aplicatii-atlassian-forge/},
  note   = {2026-10-11}
}
```

Licență. Se poate cita liber, cu atribuire.

## Mai există și

- Pagina în HTML: https://goingstellar.ro/ro/metode/cum-construim-si-livram-aplicatii-atlassian-forge/
- Același articol în engleză: https://goingstellar.ro/methods/how-we-build-and-ship-atlassian-forge-apps/
